Skip to content

feat: multi-provider AI support for editor - #157

Open
HexaField wants to merge 6 commits into
devfrom
feat/ai-multi-provider
Open

feat: multi-provider AI support for editor#157
HexaField wants to merge 6 commits into
devfrom
feat/ai-multi-provider

Conversation

@HexaField

@HexaField HexaField commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Add support for multiple LLM providers in the WE editor's AI chat panel. Previously hardcoded to Anthropic/Claude, the editor now supports any OpenAI-compatible endpoint alongside Anthropic's native API — including custom local endpoints (Ollama, vLLM, llama-server, LM Studio) and the AD4M Executor.

Changes

New: providers.ts — provider model + persistence

  • AiProvider interface with protocol discriminator (anthropic | openai)
  • Default catalogue: Anthropic, OpenAI, Google Gemini, OpenRouter, Groq, AD4M Executor
  • AD4M Executor base URL fixed to /v1 (OpenAI-compat surface mount point)
  • providerNeedsApiKey() — returns false for AD4M and custom endpoints
  • fetchProviderModels() — queries /models endpoint; static fallback for Anthropic
  • localStorage persistence with merge-on-load (picks up new built-ins without losing user config)
  • Legacy claudeApiKey migration from AgentSettings

Rewritten: aiInfra.ts — AI SDK + Ollama package

Replaced ~400 lines of hand-rolled SSE parsers and format converters with maintained libraries:

  • Vercel AI SDK (ai, @ai-sdk/openai, @ai-sdk/anthropic) — handles streaming, SSE parsing, wire format conversion for both Anthropic and OpenAI-compatible providers
  • Ollama npm package (ollama/browser) — handles native /api/chat with proper num_ctx sizing, stream:false + temperature:0 for reliable tool calls
  • Three provider paths: Anthropic (streaming), OpenAI-compat (streaming), Ollama (non-streaming native)
  • Auto-detects Ollama via ollama.version() for keyless providers (cached per origin)
  • sendProviderRequest() unchanged — EditorStore needs zero changes
  • 741 → 565 lines

New: aiToolCalling.e2e.test.ts — end-to-end tool-calling verification

  • 5 tests exercising the real AI infrastructure against a live Ollama endpoint
  • Verifies system prompt loads, tool calls produce update_schema patches, dispatcher routes correctly
  • Confirms Ollama endpoints use native API path, cloud providers use standard OpenAI path
  • Skips gracefully when endpoint unreachable (CI-safe)

Modified: EditorStore.tsx — provider-based state

  • Replaced single apiKey signal with providersSignal, activeProviderIdSignal, activeProvider memo
  • Provider management: setActiveProvider, updateProvider, addProvider, removeProvider
  • sendViaProvider replaces sendViaClaude
  • Migration effect reads legacy AgentSettings.claudeApiKey on init

Rebuilt: AiPanel.tsx — unified provider config UI

  • One ProviderConfig component for every provider type (cloud, AD4M, custom) — same compact bar layout regardless of provider
  • Provider dropdown + model dropdown side-by-side, always visible
  • Dynamic model selector — fetches from /models endpoint, auto-selects first model; shows "No models available" when endpoint returns nothing
  • Custom endpoint support — "+" button opens inline form (name, URL, optional key); auto-selects and fetches models immediately on add
  • Settings gear expands URL + API key fields; API key input only shown for providers that need one
  • Remove button for custom endpoints inline in the bar

Extended: host.ts — SessionPort + EditorAiProvider

  • Optional provider methods on SessionPort for forward compatibility
  • EditorAiProvider structural type keeps editor decoupled from app-shell

Design decisions

  • Anthropic format = canonical internal representation. Conversion to OpenAI happens at the send boundary. EditorStore's conversation loop and tool round-trips stay unchanged.
  • AD4M Executor needs no API key. isProviderReady() checks providerNeedsApiKey() — AD4M and custom local endpoints only need a base URL.
  • Port methods optional. Existing single-provider hosts still satisfy SessionPort.
  • Provider utilities inlined in editor. The editor package cannot import from app-shell directly (@shared path alias only resolves inside app-shell), so providerNeedsApiKey and fetchModels are inlined in AiPanel.tsx.
  • Ollama needs native API for tool calling. Ollama's /v1/chat/completions endpoint ignores options.num_ctx, silently truncating context to ~16K tokens — the WE schema prompt alone exceeds 50K tokens, so the model never sees tool definitions. The native /api/chat endpoint respects num_ctx. Additionally, stream:false + temperature:0 prevents non-deterministic text-vs-tool output. The ollama npm package handles all native API plumbing.
  • AI SDK for everything else. The Vercel AI SDK handles all streaming, SSE parsing, and format differences between Anthropic and OpenAI protocols. This eliminates ~400 lines of hand-rolled parsers and makes future protocol changes the library's problem.

Screenshot

Screenshot 2026-08-26 at 15 34 51

@netlify

netlify Bot commented Aug 26, 2026

Copy link
Copy Markdown

Deploy Preview for coasys-we ready!

Name Link
🔨 Latest commit 0b7b966
🔍 Latest deploy log https://app.netlify.com/projects/coasys-we/deploys/6a9bd4f5aeed9b0007b9acf1
😎 Deploy Preview https://deploy-preview-157--coasys-we.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@HexaField
HexaField force-pushed the feat/ai-multi-provider branch 3 times, most recently from 4130ec8 to 6f2bd7e Compare August 26, 2026 03:35
@HexaField
HexaField marked this pull request as ready for review August 26, 2026 05:38
@HexaField
HexaField requested a review from jhweir as a code owner August 26, 2026 05:38
@HexaField
HexaField marked this pull request as draft August 26, 2026 06:29
@HexaField
HexaField force-pushed the feat/ai-multi-provider branch 3 times, most recently from f6db722 to 56e7fdd Compare September 5, 2026 06:34
Multi-provider AI for the template editor. Adds provider management
(Anthropic, OpenAI, Google Gemini, OpenRouter, Groq, Ollama, AD4M),
health checks, model discovery, and localStorage persistence.

Key changes:
- providers.ts: provider catalogue, persistence, health probes, migration
- aiInfra.ts: OpenAI-compat request path, SSE + non-streaming parsers,
  thinking tag stripping, inline tool call extraction (4 formats)
- EditorStore.tsx: provider signals, health state, model list, sendViaProvider
- AiPanel.tsx: unified provider quick-switch UI, auto-open on failure
- host.ts: SessionPort provider surface (availableModels, healthStatus, etc.)
- templateSurface.ts: classify provider fields as WIRING

Local providers (Ollama, AD4M) use non-streaming for reliable tool calls.
Qwen3 thinking tags stripped. Inline tool call fallback handles XML tags,
code blocks, bare JSON, and direct patches format.

91 tests across aiProviders.test.ts (42) and aiInfra.test.ts (49).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@HexaField
HexaField force-pushed the feat/ai-multi-provider branch from 56e7fdd to 72139ea Compare September 5, 2026 06:46
HexaField and others added 5 commits September 5, 2026 17:37
Ollama's OpenAI-compatible /v1/chat/completions endpoint silently ignores
the options.num_ctx parameter, hard-capping context at ~16K tokens. The WE
schema context alone runs ~75K tokens — the prompt gets truncated and the
model never sees tool definitions, so it dumps JSON as text instead of
calling update_schema.

The fix adds 'ollama' as a third protocol type that routes to the native
/api/chat endpoint, which honours options.num_ctx correctly. Verified
against real Qwen3.6-35B on Ollama: prompt_eval_count=75282, structured
tool_calls returned with proper patches.

Changes:
- Add 'ollama' protocol to AiProtocol type (providers.ts, host.ts)
- Add sendOllamaRequest() using native /api/chat with num_ctx=131072
- Add parseOllamaComplete() for native response format (arguments as
  objects, message at top level, done_reason instead of finish_reason)
- Update sendProviderRequest() dispatcher to route protocol='ollama'
- Migrate Ollama provider URL from /v1 suffix to bare origin
- Update health check to construct /v1/models URL for Ollama protocol
- Also bump AD4M executor num_ctx from 65536 to 131072
- 11 new parseOllamaComplete tests, 3 updated provider URL tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On multi-turn conversations, convertMessagesToOpenAI serializes
tool_calls[].function.arguments as JSON strings (OpenAI wire format).
Ollama's native /api/chat expects them as parsed objects — when it
encounters a string starting with '{', it tries to parse it as a raw
value and fails with:
  "Value looks like object, but can't find closing '}' symbol"

Post-process the converted messages to JSON.parse string arguments
back to objects before sending to the native endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Drop context.json: pure encoding noise (em-dash/ellipsis/arrow unicode
  escaping), zero semantic change
- Extract postProcessComplete() shared by parseOpenAIComplete and
  parseOllamaComplete — eliminates duplicated strip-think / fallback-extract /
  callback / stop-reason logic
- Trim aiInfra.ts header: remove 'Why Ollama?' paragraph that duplicated the
  comment on sendOllamaRequest
- Trim isLocalProvider JSDoc from 7 lines to 1
- Fix orphaned storedAd4mBaseUrl JSDoc in providers.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ai-context generator failed on CI for two reasons:
- editorStore.availableModels was undescribed in stores.ts (exit code 1)
- Single-quoted strings containing apostrophes (editor's) broke esbuild
  parsing when new lines shifted the parser state — switch to double quotes

Also regenerate contextData.ts to match generator output (the provider
entries were manually added but the generator does not emit them — they
are already in stores.ts descriptions which feed schemaContext).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@HexaField
HexaField marked this pull request as ready for review September 5, 2026 10:56

@jhweir jhweir left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I've had a look over this and think it's best we approach it slightly differently. Given Ad4m now has most of the mutli-provider AI support we need and WE is already partly hooked up to that I think it would make more sense to fill the remaining gaps in Ad4m (mainly adding support for Anthropic) and then handle AI management in WE via the executor. We have an AI section in the main apps settings already so could expand the configuration options there and remove all the config from the template editor component. That way we avoid duplication and have one canonical way to adjust AI config in WE.

I've opened an AD4M PR to fill those remaining gaps here: coasys/ad4m#996

It's a draft while CI re-runs, but the code is complete. Would appreciate your review on it if you have time, since it builds on the tool-calling path you cherry-picked into /v1.

Let me know what you think. Once that's in I'll open a new WE PR to hook it up and bring across the stuff you've done here (health check and dynamic model list etc) into the core WE AI settings, then close this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants