Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions VERDICT-FORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@ the client to providers' billing APIs).
<p align="center"><img src=".verdict/tui-verdict-binary.png" alt="Compiled VERDICT binary" width="720"></p>
<p align="center"><sub>The compiled standalone <code>verdict</code> binary — VERDICT wordmark + titlebar, no auto-update dialog.</sub></p>

## Local LLM / Ollama OpenAI-compat

caseforge drives this runtime with a custom `@ai-sdk/openai-compatible` provider
whose `options.baseURL` is `VERDICT_LLM_BASEURL`. The SDK POSTs
`${baseURL}/chat/completions`.

| `VERDICT_LLM_BASEURL` | Request path | Typical Ollama response |
|---|---|---|
| `http://host:11434/v1` | `/v1/chat/completions` | OpenAI-shaped JSON (success or `model … not found`) |
| `http://host:11434` (no `/v1`) | `/chat/completions` | plain `404 page not found` |

This fork does **not** rewrite bare Ollama roots — missing `/v1` is a caller
config issue (normalize in caseforge / env). Residual checks:

```bash
bash scripts/check-httpapi-error-contracts.sh # always offline
# optional live probe (SKIP when host unset/down):
VERDICT_LLM_BASEURL=http://host:11434/v1 bash scripts/check-ollama-openai-compat.sh
```

## Build & run

Same as upstream (Bun ≥ 1.3.14, no Go needed):
Expand Down
50 changes: 50 additions & 0 deletions packages/opencode/test/provider/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,56 @@ it.instance(
},
)

// Ollama OpenAI-compat: @ai-sdk/openai-compatible POSTs ${baseURL}/chat/completions.
// Ollama only serves that under /v1; a bare host:port root yields "404 page not found".
// This runtime does not auto-append /v1 — callers (caseforge VERDICT_LLM_BASEURL) must.
it.instance(
"Ollama-style openai-compatible baseURL with /v1 is preserved on the provider",
Effect.gen(function* () {
const providers = yield* list
const id = ProviderV2.ID.make("ollama-local")
expect(providers[id].options.baseURL).toBe("http://10.126.60.100:11434/v1")
expect(providers[id].models["local"].api.npm).toBe("@ai-sdk/openai-compatible")
}),
{
config: {
provider: {
"ollama-local": {
name: "Ollama local",
npm: "@ai-sdk/openai-compatible",
env: [],
models: { local: { name: "Local", tool_call: true, limit: { context: 8192, output: 2048 } } },
options: { apiKey: "local", baseURL: "http://10.126.60.100:11434/v1" },
},
},
},
},
)

it.instance(
"Ollama-style openai-compatible baseURL without /v1 is not rewritten by the provider",
Effect.gen(function* () {
const providers = yield* list
const id = ProviderV2.ID.make("ollama-bare")
// Intentionally bare: documents that missing /v1 is a caller config bug, not engine path join.
expect(providers[id].options.baseURL).toBe("http://10.126.60.100:11434")
expect(String(providers[id].options.baseURL).endsWith("/v1")).toBe(false)
}),
{
config: {
provider: {
"ollama-bare": {
name: "Ollama bare root",
npm: "@ai-sdk/openai-compatible",
env: [],
models: { local: { name: "Local", tool_call: true, limit: { context: 8192, output: 2048 } } },
options: { apiKey: "local", baseURL: "http://10.126.60.100:11434" },
},
},
},
},
)

// Edge cases for model configuration

it.instance(
Expand Down
68 changes: 68 additions & 0 deletions scripts/check-ollama-openai-compat.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# Optional smoke: Ollama OpenAI-compat /v1/chat/completions is reachable.
#
# Distinguishes:
# - correct base (.../v1) → OpenAI-shaped error JSON for unknown model (route exists)
# - bare root (no /v1) → plain "404 page not found" (the m13 agent-path failure mode)
#
# Offline-friendly: SKIP (exit 0) when host is unset or unreachable.
# Usage:
# VERDICT_LLM_BASEURL=http://host:11434/v1 bash scripts/check-ollama-openai-compat.sh
# OLLAMA_HOST=http://host:11434 bash scripts/check-ollama-openai-compat.sh
set -euo pipefail

raw="${VERDICT_LLM_BASEURL:-${OLLAMA_HOST:-${CASEFORGE_SPARK_ENDPOINT:-}}}"
if [[ -z "${raw}" ]]; then
echo "SKIP: set VERDICT_LLM_BASEURL, OLLAMA_HOST, or CASEFORGE_SPARK_ENDPOINT"
exit 0
fi

# Strip trailing slash; strip a single trailing /v1 for origin probes.
origin="${raw%/}"
origin="${origin%/v1}"
v1_chat="${origin}/v1/chat/completions"
Comment on lines +21 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject bare VERDICT_LLM_BASEURL in smoke test

When VERDICT_LLM_BASEURL is set to the misconfigured bare Ollama root (http://host:11434), this script strips/normalizes it into origin and then probes ${origin}/v1/chat/completions, so it reports PASS even though the runtime would still POST to ${VERDICT_LLM_BASEURL}/chat/completions and hit the documented 404 page not found. Since this smoke check is meant to catch that exact missing-/v1 configuration, validate the supplied base URL (or probe the raw configured request path) before constructing the corrected /v1 URL.

Useful? React with 👍 / 👎.

bare_chat="${origin}/chat/completions"
tags="${origin}/api/tags"

if ! curl -sS -m 2 -o /dev/null "${tags}"; then
echo "SKIP: Ollama unreachable at ${origin} (tags probe failed)"
exit 0
fi

body='{"model":"__opencode_ollama_smoke_missing__","messages":[{"role":"user","content":"ping"}]}'

v1_body="$(mktemp)"
bare_body="$(mktemp)"
trap 'rm -f "${v1_body}" "${bare_body}"' EXIT

v1_code="$(curl -sS -m 5 -o "${v1_body}" -w "%{http_code}" \
-X POST "${v1_chat}" -H "Content-Type: application/json" -d "${body}")"
bare_code="$(curl -sS -m 5 -o "${bare_body}" -w "%{http_code}" \
-X POST "${bare_chat}" -H "Content-Type: application/json" -d "${body}")"

v1_text="$(cat "${v1_body}")"
bare_text="$(cat "${bare_body}")"

# Route under /v1 must not be the Go/Ollama plain page-not-found body.
if [[ "${v1_text}" == *"404 page not found"* ]]; then
echo "FAIL: ${v1_chat} returned plain page-not-found (http=${v1_code})"
echo " body: ${v1_text}"
exit 1
fi

# Accept any non-page-not-found response that looks like OpenAI-compat JSON error or success.
if [[ "${v1_text}" != *"error"* && "${v1_text}" != *"choices"* ]]; then
echo "FAIL: ${v1_chat} unexpected body (http=${v1_code}): ${v1_text}"
exit 1
fi

# Document the bare-root failure mode (expected when baseURL omits /v1).
if [[ "${bare_text}" != *"404 page not found"* ]]; then
echo "WARN: bare ${bare_chat} did not return page-not-found (http=${bare_code}); body=${bare_text}"
fi

echo "PASS: Ollama OpenAI-compat reachable"
echo " origin=${origin}"
echo " v1_chat=${v1_chat} http=${v1_code}"
echo " bare_chat=${bare_chat} http=${bare_code} (expect page-not-found when /v1 omitted from baseURL)"
echo " note: @ai-sdk/openai-compatible POSTs \${baseURL}/chat/completions — baseURL must include /v1"
Loading