Skip to content

feat(kernel-language-model-service): Add language model client#876

Merged
grypez merged 20 commits intomainfrom
grypez/language-model-client
Apr 1, 2026
Merged

feat(kernel-language-model-service): Add language model client#876
grypez merged 20 commits intomainfrom
grypez/language-model-client

Conversation

@grypez
Copy link
Copy Markdown
Contributor

@grypez grypez commented Mar 16, 2026

Summary

Adds a client constructor to @ocap/kernel-language-model-service, allowing wiring ollama or any OpenAI-compatible language model server into the kernel as a first-class kernel service; and extends @ocap/kernel-agents with a chat-completion-based agent that uses the standard tool-calling interface. Verified end-to-end with integration and e2e tests in @ocap/kernel-test-local.

feat(kernel-language-model-service): Add /v1 API support and language model client

Language model service supported backends:

  • Open /v1: The OpenAI /v1/chat/completions API is de facto standard across industry, as evidenced by spaghetti compatibility across LiteLLM, Ollama, llama.cpp, and anthropic.
  • Ollama: Finer detail is possible with the ollama raw completions API, which generates raw token streams instead of structured chat objects. The REPL strategies depend upon such support.

Vat clients (src/client.ts): makeChatClient returns an OpenAI-SDK-shaped object (client.chat.completions.create(...)) that routes calls through a CapTP ChatService reference via E(). makeSampleClient is the equivalent for raw token sampling.

Test utilities (src/test-utils/): Replaces the previous queue-based mock model with makeMockOpenV1Fetch — a deterministic SSE mock that emits a configured sequence of token strings, keeping integration tests CI-safe and fast. makeMockSample provides the same for the sample path.

Also updates kernel-test to use the new chat/sample API: replaces lms-user-vat / lms-queue-vat with lms-chat-vat / lms-sample-vat and the corresponding test files.

feat(kernel-agents): chat-completion-based agent with tool-calling

Adds makeChatAgent to @ocap/kernel-agents (exported as @ocap/kernel-agents/chat), a capability-augmented agent built on the chat completions API. Capabilities are passed as tools, responses arrive via tool_calls, and results are returned as role: "tool" messages.

test(kernel-test-local): kernel-LMS integration tests

Adds two test variants that share a common runLmsChatKernelTest helper:

  • lms-chat.test.ts: injects makeMockOpenV1Fetch — no network, runs under test:dev.
  • lms-chat.e2e.test.ts (local): uses real fetch against a local Ollama instance, run via test:e2e:local.

Both launch a subcluster with lms-chat-vat through the kernel, register the LMS kernel service, and assert that the vat logs the model's response.

Also adds agents.e2e.test.ts exercising chat/json/repl agents end-to-end, and test/suite.test.ts as a language-model-service pre-flight check, whether using ollama, llama-cpp, lite-llm, etc.

Package layout is conformed to kernel-test: all vats, helpers, and test files live under src/; test/ holds only the pre-flight suite.

Test plan

  • yarn workspace @ocap/kernel-test-local test:dev:quiet — unit + integration tests pass (no Ollama required)
  • yarn workspace @ocap/kernel-language-model-service test:dev:quiet — all klms unit tests pass
  • yarn workspace @ocap/kernel-agents test:dev:quiet — all kernel-agents unit tests pass
  • With Ollama running and llama3.2:3b pulled: yarn workspace @ocap/kernel-test-local test:e2e:local
  • Or, with llama.cpp on localhost:8080 and glm-4.7-flash pulled and served, yarn workspace @ocap/kernel-test-local test:e2e:local:llama-cpp

Note

Medium Risk
Moderate risk due to introducing new public exports and wiring for chat/tool-calling plus new Open /v1 and expanded Ollama service paths, which could affect downstream integrations and streaming behavior.

Overview
Adds a first-class chat-completions client layer to @ocap/kernel-language-model-service (makeChatClient/makeSampleClient) plus a kernel-service wrapper (makeKernelLanguageModelService) so vats can call LMS backends via a stable /v1-style API.

Introduces an OpenAI-compatible /v1 Node.js backend with parameter/response validation, JSON stripping, and SSE streaming support, and expands the Ollama backend to support chat and both streaming and non-streaming raw sample requests (including tool-call argument parsing).

Extends @ocap/kernel-agents with a new makeChatAgent strategy (exported as @ocap/kernel-agents/chat) that drives tool-calling loops, validates tool args via new kernel-utils JSON-schema→Superstruct helpers, and records chat turns as experiences; updates local and kernel tests to exercise the new service/client paths and replaces the old queue-based LMS test utils with deterministic makeMockOpenV1Fetch/makeMockSample.

Written by Cursor Bugbot for commit 6b23244. This will update automatically on new commits. Configure here.

@grypez grypez force-pushed the grypez/language-model-client branch from f2a4ffd to e3cf0b3 Compare March 16, 2026 14:26
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 16, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 78.29%
⬆️ +0.16%
8487 / 10840
🔵 Statements 78.11%
⬆️ +0.16%
8623 / 11039
🔵 Functions 75.85%
⬆️ +0.31%
1979 / 2609
🔵 Branches 76.07%
⬇️ -0.55%
3641 / 4786
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/kernel-agents/src/capabilities/validate-capability-args.ts 100% 100% 100% 100%
packages/kernel-agents/src/strategies/chat-agent.ts 90.9% 87.5% 100% 90.32% 141, 184-192
packages/kernel-language-model-service/src/client.ts 100% 100% 100% 100%
packages/kernel-language-model-service/src/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-language-model-service/src/kernel-service.ts 100% 100% 100% 100%
packages/kernel-language-model-service/src/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-language-model-service/src/ollama/base.ts 94.11%
⬇️ -5.89%
68.75%
⬇️ -31.25%
100%
🟰 ±0%
94.11%
⬇️ -5.89%
82, 88-92, 236
packages/kernel-language-model-service/src/ollama/nodejs.ts 70%
⬇️ -30.00%
100%
🟰 ±0%
50%
⬇️ -50.00%
70%
⬇️ -30.00%
61-63
packages/kernel-language-model-service/src/ollama/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-language-model-service/src/open-v1/base.ts 94.33% 75% 100% 94.23% 131, 145-146
packages/kernel-language-model-service/src/open-v1/nodejs.ts 83.33% 50% 100% 83.33% 31
packages/kernel-language-model-service/src/open-v1/normalize-stream-chunk.ts 90% 75% 100% 90% 49-51
packages/kernel-language-model-service/src/open-v1/response-json.ts 100% 100% 100% 100%
packages/kernel-language-model-service/src/open-v1/strip-open-v1-json.ts 68.57% 52.7% 91.66% 68.57% 12, 22-35, 45, 49-51, 58, 63-69, 80, 96, 114, 131, 145, 159, 168, 186, 208, 226
packages/kernel-language-model-service/src/open-v1/types.ts 100% 100% 100% 100%
packages/kernel-language-model-service/src/test-utils/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-language-model-service/src/test-utils/mock-fetch.ts 0% 0% 0% 0% 9-35
packages/kernel-language-model-service/src/test-utils/mock-sample.ts 0% 0% 0% 0% 9-16
packages/kernel-language-model-service/src/utils/parse-tool-arguments.ts 100% 77.77% 100% 100%
packages/kernel-test-local/src/constants.ts 90.9%
⬆️ +7.57%
83.33%
⬇️ -16.67%
0%
🟰 ±0%
100%
🟰 ±0%
37
packages/kernel-test-local/src/lms-chat.ts 100% 100% 100% 100%
packages/kernel-test-local/src/utils.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-test-local/src/vats/lms-chat-vat.ts 0% 0% 0% 0% 22-38
packages/kernel-test/src/vats/lms-chat-vat.ts 0% 0% 0% 0% 22-40
packages/kernel-test/src/vats/lms-sample-vat.ts 0% 0% 0% 0% 24-32
packages/kernel-utils/src/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-utils/src/json-schema-to-struct.ts 80.95% 78.12% 100% 80.95% 30, 40, 45-48, 91-92, 111
Generated in workflow #4147 for commit 6b23244 by the Vitest Coverage Report Action

@grypez grypez force-pushed the grypez/language-model-client branch from e3cf0b3 to 5296cb9 Compare March 16, 2026 16:17
@grypez grypez marked this pull request as ready for review March 16, 2026 16:32
@grypez grypez requested a review from a team as a code owner March 16, 2026 16:32
@grypez
Copy link
Copy Markdown
Contributor Author

grypez commented Mar 16, 2026

@cursoragent review this PR for inconsistencies across docstrings, documentation and naming conventions

@cursor
Copy link
Copy Markdown

cursor bot commented Mar 16, 2026

Unable to authenticate your request. Please make sure to connect your GitHub account to Cursor. Go to Cursor

@grypez grypez force-pushed the grypez/language-model-client branch from 2dffa25 to ea9f656 Compare March 17, 2026 19:51
@rekmarks rekmarks marked this pull request as draft March 26, 2026 17:38
@grypez grypez force-pushed the grypez/language-model-client branch 4 times, most recently from 216052f to a21e0ed Compare March 30, 2026 20:35
@grypez grypez marked this pull request as ready for review March 30, 2026 20:54
grypez and others added 7 commits March 30, 2026 17:45
Add @ocap/kernel-language-model-service — a new package that bridges the
kernel with OpenAI-compatible language model backends.

Core types and kernel service:
- ChatParams / ChatResult / ChatService types for request/response shapes
- makeKernelLanguageModelService: registers a kernel service object that
  dispatches chat calls to a provided chat function
- LANGUAGE_MODEL_SERVICE_NAME constant

Backends:
- Open /v1 backend with SSE streaming (makeOpenV1NodejsService)
- Ollama Node.js backend (OllamaNodejsService)

Client API:
- makeChatClient: returns an OpenAI-SDK-compatible client object that
  routes chat.completions.create() calls through a CapTP ChatService
- makeSampleClient: convenience wrapper for single-token sampling

Test utilities:
- makeMockOpenV1Fetch: deterministic SSE mock for unit/integration tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add an integration test and e2e test that exercise the full
kernel → LMS service → Ollama round-trip via a bundled vat.

- lms-chat-vat: bundled vat that sends a single chat message and logs
  the response, used to verify the round-trip through the kernel
- lms-chat.ts: shared test helper (runLmsChatKernelTest)
- lms-chat.test.ts: uses a mock fetch — CI-safe, no network
- lms-chat.e2e.test.ts: uses real fetch against local Ollama
- agents.e2e.test.ts: json/repl agent e2e tests against local Ollama
- test/suite.test.ts: pre-flight check that Ollama is running

Lay out the package to match kernel-test: all vats, helpers, and test
files live under src/; test/ holds only the Ollama pre-flight suite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a chat-based capability-augmented agent alongside the existing
text-completion JSON agent. Uses ChatService.chat() with a messages[]
array instead of LanguageModel.sample() with a raw prompt string.

- makeChatAgent({ chat, capabilities }) implements the Agent interface
- Capabilities are described to the model via a JSON system prompt
- Model signals completion by invoking the auto-injected 'end' capability
- Capability results are injected as 'user' turns for model-provider compatibility
- Exported as @ocap/kernel-agents/chat, mirroring @ocap/kernel-agents/json

Usage:
  const client = makeChatClient(lmsServiceRef, 'qwen2.5:0.5b');
  const agent = makeChatAgent({
    chat: (messages) => client.chat.completions.create({ messages }),
    capabilities: { walletBalance, walletSend },
  });
  const result = await agent.task('what is my balance?');

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gent

Replace the custom JSON-in-system-prompt approach with the standard chat
completions tool-calling interface: capabilities are passed as `tools`,
responses arrive via `tool_calls`, and results are returned as `role:
"tool"` messages. Add a `glm-4.7-flash` / llama.cpp e2e test and a
`LMS_PROVIDER` constant so the suite can be aimed at either Ollama or
llama.cpp without OOM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@grypez grypez force-pushed the grypez/language-model-client branch from a21e0ed to 70ae122 Compare March 30, 2026 21:45
Copy link
Copy Markdown
Contributor

@sirtimid sirtimid left a comment

Choose a reason for hiding this comment

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

Nice! Some comments to address.

* @param params - The chat parameters.
* @returns A promise resolving to the full chat result.
*/
async #nonStreamingChat(params: ChatParams): Promise<ChatResult> {
Copy link
Copy Markdown
Contributor

@sirtimid sirtimid Mar 31, 2026

Choose a reason for hiding this comment

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

#nonStreamingChat, #streamingChat, and listModels never check response.ok. A 401/429/500 will produce cryptic JSON.parse or .map() errors instead of a clear HTTP error. This is a real production concern with LLM APIs (rate limits, auth failures).

Made-with: Cursor
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

@grypez grypez requested a review from sirtimid April 1, 2026 02:33
@grypez grypez enabled auto-merge April 1, 2026 02:33
Co-authored-by: Dimitris Marlagkoutsos <info@sirtimid.com>
Copy link
Copy Markdown
Contributor

@sirtimid sirtimid left a comment

Choose a reason for hiding this comment

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

LGTM!

@grypez grypez added this pull request to the merge queue Apr 1, 2026
Merged via the queue into main with commit 4fa6147 Apr 1, 2026
32 checks passed
@grypez grypez deleted the grypez/language-model-client branch April 1, 2026 14:12
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