Skip to content

feat: add Ollama as a first-run model provider - #163

Open
itsmunzir wants to merge 1 commit into
CopilotKit:mainfrom
itsmunzir:feat/ollama-first-run
Open

itsmunzir wants to merge 1 commit into
CopilotKit:mainfrom
itsmunzir:feat/ollama-first-run

Conversation

@itsmunzir

@itsmunzir itsmunzir commented Oct 18, 2026 •

Copy link
Copy Markdown

Summary

OpenDots can use a local Ollama model without an API key. Set MODEL_PROVIDER=ollama and OLLAMA_MODEL in .env; OLLAMA_BASE_URL defaults to http://localhost:11434/v1. Ollama serves an OpenAI-compatible Chat Completions API, so Dot turns and research briefs use the existing TanStack AI openaiCompatibleText adapter and research request unchanged. OpenAI stays the default, and its settings resolve exactly as before.

The first-run setup card and Settings & setup now show the selected model provider. On a fresh install they list both options: OpenAI with OPENAI_API_KEY and OPENAI_MODEL, or Ollama with MODEL_PROVIDER=ollama and OLLAMA_MODEL. With Ollama selected, Check Ollama asks the server to list the endpoint's models. The result appears as one status block under the button: an icon and a one-line headline, then at most one short next step. For example, "Ollama isn't running at http://localhost:11434/v1." followed by "Start it (open the Ollama app or run ollama serve), then check again."

Closes #100. Related to #118 and #87: this adds a local model option. Conversations still require CopilotKit Intelligence.

Changes

  • modelConfigFromEnv in src/server/platform-config.ts resolves the provider, key, model and base URL. Startup fails with a clear message for an unknown MODEL_PROVIDER or an OLLAMA_BASE_URL that is not http(s) or contains credentials, query or fragment. A base URL without a path, such as Ollama's OLLAMA_HOST form http://localhost:11434, uses /v1.
  • No API key is required with Ollama. The OpenAI-compatible client sends the placeholder bearer token ollama, and OPENAI_API_KEY is never forwarded to the Ollama endpoint.
  • setupStatus reports OLLAMA_MODEL as the only model setting Ollama needs, and stops asking for OPENAI_API_KEY when Ollama is selected. It adds modelProvider, plus modelBaseUrl for Ollama only. OPENAI_BASE_URL may point to a private proxy, so it stays server-side.
  • New owner-authenticated GET /api/setup/model (src/server/model-check.ts): it calls GET {baseUrl}/models from the server with a 5-second timeout. It returns a one-sentence message, an optional one-line hint (backticks mark code), and the models worth offering: every model when the configured one is missing, otherwise the others (at most 50). None of these contain credentials or the provider's response body. Ollama's name and name:latest match each other. The UI only shows the button for OpenAI once a key and model are configured.
  • New ModelSetup component rendered in the setup card and the Settings service-setup note. It follows the existing design system:
    • The outlined button shares the .connection-connect style. Its label reads "Check Ollama", then "Checking…" with the LoaderCircle/spin spinner, then "Check again".
    • Code uses the existing Settings code chip, now also applied to the setup card.
    • Colours are the template ink, #438c81 teal for success and the editor's save-error #9b4a32. Because they're hex values, the build step derives the dark theme.
    • Accessibility:
      • Success and problem states carry different icons (CircleCheck and CircleAlert), so status isn't conveyed by colour alone.
      • It's a native type="button", so it doesn't submit the Settings form. While checking it sets aria-busy and aria-disabled rather than disabled, so keyboard focus stays on it, and repeat presses are ignored.
      • Results go to a persistent role="status" / aria-live="polite" region that the button references with aria-describedby. The region is cleared before each check so a repeated result is announced again.
    • Motion: the result fades in over 180 ms. The existing reduced-motion rule turns this off.
  • Compose forwards MODEL_PROVIDER, OLLAMA_BASE_URL and OLLAMA_MODEL. .env.example, docs/SETUP.md (new Model provider section and connection-settings rows) and the README get-started and data-and-privacy text describe the option.

Configuration lives in .env, like the other providers. I didn't add a script or UI control that writes .env: the server reads it at startup, and the existing setup flow already tells the owner to edit it and restart.

How to try it with Ollama

ollama pull llama3.2      # any model with tool-calling support
MODEL_PROVIDER=ollama
OLLAMA_MODEL=llama3.2
# OLLAMA_BASE_URL=http://localhost:11434/v1

Then run npm run dev, open http://127.0.0.1:5173, and select Check Ollama on the setup card or in Settings & setup. To see the error states, stop Ollama, or set OLLAMA_MODEL to a model you haven't pulled. To chat, add an Intelligence key as before (hosted or local evaluation).

Validation

  • New tests/model-provider.test.ts:
    • OpenAI resolution unchanged, including OPENAI_BASE_URL passthrough.
    • Ollama resolution without a key, and the OpenAI key never reaching Ollama.
    • Base-URL normalization and rejection; no credentials in the error.
    • Provider-aware missing settings.
    • Model check against a local HTTP fixture: listed, untagged :latest match, not pulled, no models, server stopped, 404, and a non-JSON body.
    • The OpenAI no-key path makes no request, and a 401 doesn't echo the key.
    • The route requires the owner token and /api/workspace exposes the provider.
    • A Dot turn through DotAgent posts to http://localhost:11434/v1/chat/completions with Bearer ollama and the Ollama model.
  • New tests/model-setup.test.tsx:
    • The provider choice on a fresh install, and the keyless Ollama display with the outlined button.
    • The live region and aria-describedby wiring.
    • Success: a single headline, plus "Also available" only when other models exist.
    • Not running: problem state, icon, one next step.
    • Model not pulled: next step plus available models.
    • A failed request.
    • Labels move from "Check Ollama" to "Checking…" (aria-busy) to "Check again", with focus kept and repeat presses ignored. This test caught a double-request bug, now fixed.
  • npm run check-format, npm run lint, npm run typecheck, npm test (56 files, 363 tests) and npm run build pass on Node 24.18.0.
    • Under heavy machine load, one full run hit 5-second timeouts in the unrelated tests/telemetry.test.ts. That file passes on its own, and a rerun of the full suite passed.

With real Ollama (0.40.5 on macOS, model llama3.2:1b)

  • npm run dev with only MODEL_PROVIDER=ollama and OLLAMA_MODEL=llama3.2:1b, using the default http://localhost:11434/v1:

    • /api/workspace reported the Ollama provider with no missing model setting. Only the Intelligence key was missing.
    • Check Ollama, focused and pressed with Enter, showed "Ollama is running and has llama3.2:1b.".
  • With OLLAMA_MODEL=llama3.2, a tag that isn't pulled locally, the check showed "Ollama doesn't have llama3.2 yet." and "Run ollama pull llama3.2, then check again.", followed by the available models.

  • With OLLAMA_BASE_URL pointing to a port where nothing listens, the check showed "Ollama isn't running at …" and the one-line start instruction. I didn't stop the running Ollama to test this.

  • Each state was checked in light and dark themes at 1280 px and 375 px, with no horizontal overflow. Measured WCAG contrast (light / dark):

    Element Light Dark
    Body text 5.11:1 4.83:1
    Success headline 11.53:1 8.16:1
    Problem headline and icon 5.88:1 6.49:1
    Success icon 3.79:1 6.91:1
    Button label 12.04:1 8.43:1
    Code 15.52:1 9.98:1

    The button border matches the existing outlined button (1.18:1 against the card); its text label identifies the control.

  • A Dot turn run directly through DotAgent (the same TanStack adapter and tools the runtime uses) against real Ollama streamed a reply. It also completed a full tool round trip: the model called create_space_page, the app executed it and returned the result, and the model continued.

    • llama3.2:1b passed malformed tool arguments, so no page was created, and it sometimes wrote tool-call JSON as plain text. These are model-quality limits; the docs recommend a model with tool support.
  • A direct max_completion_tokens request to this Ollama version returned normally.

With a mock endpoint

  • The same UI flow, plus a check that requests carry Authorization: Bearer ollama, against a local mock of /v1/models and /v1/chat/completions.
1-before-check-desktop-light 2-checking-desktop-light 3-ok-desktop-light 4-model-not-pulled-desktop-light 5-not-running-mobile-dark settings-3-ok-dark

Not verified

  • I didn't run a conversation through the browser chat. That path needs Intelligence (hosted or local Docker evaluation), which wasn't configured. The model call inside a turn is the DotAgent path tested above, and conversation persistence isn't changed.
  • I only tested llama3.2:1b. Larger tool-capable models (for example llama3.2 3B or qwen3) weren't tested.
  • I didn't test older Ollama versions.
  • I didn't run the Compose path. The host.docker.internal guidance in the docs comes from Docker Desktop's documented behavior, not from a test.
  • The build prints its existing large-chunk warning.

I made this change with help from Claude Code: it drafted the code and tests and ran the checks above, and the diff was reviewed before this PR was opened.

Select a local Ollama model with MODEL_PROVIDER=ollama and OLLAMA_MODEL.
OLLAMA_BASE_URL defaults to http://localhost:11434/v1 and no API key is
required; OpenAI-compatible clients receive a placeholder bearer token and
the OpenAI key is never forwarded. OpenAI remains the default and its
settings resolve exactly as before.

The setup card and Settings show the selected provider and offer a
server-side check that lists the endpoint's models and explains when
Ollama is not running or the configured model has not been pulled.

Closes CopilotKit#100
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.

Add Ollama configuration to first-run setup

1 participant