somm explained · 05

Agents and Integrations

somm is designed to be grafted on, not built around. Two compatibility shims absorb existing call sites, a LangChain chat model absorbs agent frameworks, a 14-tool MCP server hands the accumulated telemetry to coding agents, and a packaged skill teaches those agents to instrument their own calls.

Every surface on this page converges on the same two primitives: SommLLM.generate() for execution and the project-local SQLite repository for memory. The shims and the LangChain adapter feed calls into that pipeline; the MCP server and the agent skill read intelligence back out of it. Nothing here adds a second data path — a call made through the OpenAI shim lands in the same immutable calls ledger as one made natively, and an MCP comparison run records ordinary telemetry like any other call (packages/somm-mcp/src/somm_mcp/server.py:663).

flowchart LR
    subgraph adopt["Existing code plugs in"]
        shim["OpenAI shim
openai_chat_completions()"] compat["GenericLLMCompat
legacy-wrapper drop-in"] lc["SommChatModel
LangChain / LangGraph"] end subgraph agents["Coding agents plug in"] mcp["somm-mcp
14 stdio tools"] skill["somm-skill
SKILL.md + SOMMELIER.md"] end gen["SommLLM.generate()
routing / budgets / hooks"] db[("project SQLite
calls / evals / decisions")] global[("global cross-project
decision repository")] shim --> gen compat --> gen lc --> gen mcp -->|"compare / replay"| gen skill -. "teaches agents to call" .-> gen gen --> db mcp <--> db mcp -. "best-effort mirror" .-> global

Drop-in adoption: the OpenAI shim and legacy-wrapper compat layer

The examples/ area exists to prove a claim: adding somm to an existing Python application should take minimal code changes (examples/README.md:3). It demonstrates two adoption paths, one for codebases with their own LLM wrapper class and one for codebases written against the OpenAI SDK shape.

For the wrapper case, GenericLLMCompat replaces the original LLM class at import time. Calls to .generate() keep the result fields the surrounding code already reads, while cost and provenance metadata arrive as additions rather than replacements (examples/drop_in_wrapper.py:33, examples/drop_in_wrapper.py:36). For batch work, .probe_providers(n) returns provider slots so a job can be striped across whichever providers are actually available (examples/drop_in_wrapper.py:62). Each client is explicitly closed in a try/finally block — a lifecycle pattern the examples apply consistently (examples/drop_in_wrapper.py:39).

The OpenAI shim, openai_chat_completions(...), accepts chat-completion arguments and preserves the access pattern existing code depends on — resp.choices[0].message.content still works (examples/openai_swap_in.py:21, examples/openai_swap_in.py:24). Somm-specific metadata is exposed as optional extra attributes on the response, so nothing downstream has to change to ignore it (examples/openai_swap_in.py:36).

The third example shows the privacy machinery working through a shim-shaped call site. A sensitive workload is registered once with PrivacyClass.PRIVATE; every subsequent call using that workload is restricted to local providers (examples/private_workload.py:18, examples/private_workload.py:27).

Fail closed, not open If no local provider is available for a private workload, the call fails rather than sending data upstream, and attempting shadow evaluation on it raises SommPrivacyViolation (examples/private_workload.py:27, examples/private_workload.py:36). The example layers a zero-dollar daily budget on top as a second, independent safeguard (examples/private_workload.py:24).

Once a shim is in place, the accumulated record is inspectable immediately: somm status and somm tail read the telemetry, and somm serve brings up the dashboard (examples/README.md:70, examples/README.md:78).

SommChatModel: LangChain and LangGraph without leaving somm

somm-langchain lets LangChain, LangGraph, and Deep Agents applications use somm as a standard chat model, preserving LangChain call sites while somm handles routing, telemetry, cost tracking, fallback, and model memory (packages/somm-langchain/README.md:3). SommChatModel subclasses LangChain's BaseChatModel; during generation it extracts and joins system messages, translates human, assistant, and tool messages into somm's provider-neutral format, and calls SommLLM.generate() with the workload, routing pins, sampling settings, and tools (packages/somm-langchain/src/somm_langchain/chat_model.py:79).

The translation is bidirectional and careful about tool calling. Going in, assistant tool calls become tool_use blocks and tool results become user-side tool_result blocks, with system prompts passed separately (packages/somm-langchain/src/somm_langchain/chat_model.py:173). Coming back, the somm result becomes an AIMessage inside a ChatGeneration, carrying tool calls, provider/model provenance, latency, cost, outcome, and token usage (packages/somm-langchain/src/somm_langchain/chat_model.py:118). bind_tools() accepts LangChain-compatible tool definitions, normalizes them through OpenAI's common schema, and unwraps them to somm's neutral schema immediately before generation (packages/somm-langchain/src/somm_langchain/chat_model.py:152).

Several small decisions reward attention:

Streaming: not yet established The inspected implementation defines synchronous generation but no explicit streaming override, so package-specific streaming behavior through the LangChain adapter is currently unclear. Native streaming exists on the client itself via SommLLM.stream().

somm-mcp: telemetry, advice, compare, and replay over stdio

The somm-mcp command loads project configuration, constructs the full configured provider chain, builds a FastMCP server, and starts its stdio transport (packages/somm-mcp/src/somm_mcp/cli.py:27). build_server opens the project's SQLite-backed Repository, indexes the supplied providers by name, and registers closures as MCP tools (packages/somm-mcp/src/somm_mcp/server.py:59). The result: an MCP-capable coding agent can inspect LLM usage, manage workloads and prompts, compare or replay model calls, and reuse routing decisions — without a commercial service anywhere in the hot path.

14 MCP tools registered by the current server (packages/somm-mcp/src/somm_mcp/server.py:1)
ConcernToolsRegistered at
Telemetrysomm_stats, somm_search_calls — summarize or filter recorded callsserver.py:72
Guidancesomm_recommend, somm_inbox — workload guidance and recommendation itemsserver.py:117
Recommendation lifecyclesomm_apply_recommendation, somm_dismiss_recommendationserver.py:197
Model advicesomm_advise — rank models under capability, provider, price, context, and modality constraintsserver.py:232
Decision memorysomm_record_decision, somm_search_decisionsserver.py:310
Registrationsomm_register_workload, somm_register_promptserver.py:436
Evaluationsomm_eval_promote_call — promote a sampled call into a datasetserver.py:509
Executionsomm_compare, somm_replay — side-by-side calls, or rerun a captured callserver.py:549

The intelligence-facing tools are layered on evidence, not vibes: somm_recommend combines open recommendations with model rankings from shadow evaluations, and when no evaluation data exists it falls back to model-intelligence candidates and prior decisions (packages/somm-mcp/src/somm_mcp/server.py:117). The execution tools instantiate a real SommLLM with explicit providers, record normal telemetry, and always close the client; replay additionally requires captured samples and refuses private workloads outright (packages/somm-mcp/src/somm_mcp/server.py:663).

Built for hostile inputs and missing providers Recorded response bodies returned through MCP are truncated to 4,000 characters and wrapped as untrusted data, reducing the surface for prompt injection against the calling agent (packages/somm-mcp/src/somm_mcp/server.py:49). Provider-dependent tools stay discoverable even when no providers are configured, returning structured errors instead of vanishing from the catalog (packages/somm-mcp/src/somm_mcp/server.py:28). Compare enforces configurable fan-out and token ceilings, with elevated — but still bounded — caps available (packages/somm-mcp/src/somm_mcp/server.py:577).
Known documentation drift The package README advertises 10 tools while the current server registers 14 (packages/somm-mcp/README.md:7 vs. packages/somm-mcp/src/somm_mcp/server.py:1). Trust the server. The project's security worklist also flags MCP comparison limits among the surfaces still slated for hardening (notes/SOUNDCHECK-WORKLIST-2026-07-10.md:23).

Decision memory: record a model choice once, recall it across projects

Most telemetry in somm is per-project. Decision memory is the deliberate exception: when an agent (or a person driving one) commits to a model choice, somm_record_decision writes that judgment locally and best-effort mirrors it to a global cross-project repository, where somm_search_decisions can recall it from any other project on the machine (packages/somm-mcp/src/somm_mcp/server.py:360, packages/somm-mcp/src/somm_mcp/server.py:310).

sequenceDiagram
    participant A as Coding agent
    participant M as somm-mcp
    participant L as Project repository
    participant G as Global repository
    A->>M: somm_search_decisions — recall related prior choices
    A->>M: somm_advise — rank live candidates under constraints
    M->>L: model intelligence + shadow-eval evidence
    A->>M: somm_record_decision — committed choice + rationale
    M->>L: write decision
    M--)G: best-effort mirror (cross-project recall)
  

The three-step sequence — recall, advise, record — is codified as the model-selection lifecycle in SOMMELIER.md: first recall related cross-project decisions, then request ranked live candidates through somm_advise, then record the user's committed choice along with its rationale (packages/somm-skill/src/somm_skill/SOMMELIER.md:17, packages/somm-skill/src/somm_skill/SOMMELIER.md:40, packages/somm-skill/src/somm_skill/SOMMELIER.md:110).

Past model decisions inform recommendations but are deliberately non-authoritative — model intelligence changes. — design note, packages/somm-skill/src/somm_skill/SOMMELIER.md:36

That stance matters. A decision recorded in April against last quarter's pricing and capability data should shape, not dictate, a choice made in July. The recall step surfaces precedent; the advise step re-ranks against current evidence; only then is a new decision recorded.

somm-skill: teaching coding agents to instrument their own calls

The last integration surface contains no runtime code at all. somm-skill packages two Markdown resources — onboarding instructions for coding agents working on Python projects that use somm (packages/somm-skill/README.md:3). The Python module is a docstring; consumers load or copy the bundled Markdown, including via importlib.resources in the documented Claude Code installation flow (packages/somm-skill/src/somm_skill/__init__.py:1, packages/somm-skill/README.md:19).

2Markdown resources: SKILL.md + SOMMELIER.md
0runtime dependencies (packages/somm-skill/pyproject.toml:4)
5/5held-out behavioral checks passed (skillopt/somm.md:35)

SKILL.md is the operating manual: create a client with somm.llm(), tag every generation with a stable workload, register that workload with privacy and budget controls, and mark results with typed outcomes (packages/somm-skill/src/somm_skill/SKILL.md:24, packages/somm-skill/src/somm_skill/SKILL.md:44, packages/somm-skill/src/somm_skill/SKILL.md:103). It encodes operational judgment too — CLI subscription seats are pinned-only, preserving scarce quota for grading and low-volume work rather than exposing it to hot-loop routing (packages/somm-skill/src/somm_skill/SKILL.md:124). Because it ships as plain package resources, the same guidance travels to Claude, Codex, Cursor, Windsurf, and other agent packaging formats (packages/somm-skill/README.md:34).

The guidance itself is treated as a tested artifact, not prose. scripts/score_skill.py grades a candidate SKILL.md with deterministic behavioral string and regex checks, split into training and held-out cases; only the held-out aggregate is emitted for the optimizer (scripts/score_skill.py:39, scripts/score_skill.py:133). The most recent optimization run improved the skill from 4/5 to 5/5 held-out checks by converting safety-critical rules into protected uppercase NEVER/DO NOT guardrails, then stopped because no failing case remained (skillopt/somm.md:35, skillopt/somm.md:54).

Together, the skill and the MCP server close a loop the earlier pages set up: agents write instrumented calls (skill), those calls accumulate into evidence (substrate), workers turn evidence into recommendations (intelligence loop), and the same agents read the recommendations back and record their choices (MCP). Each surface is separable; none is required for the one before it to work.

Where to go next

The Life of a Call

What actually happens when a shim or SommChatModel call reaches SommLLM.generate(): workload resolution, hooks, routing, fallback, and the telemetry write.

The Data Substrate

The SQLite ledger every integration shares — and the privacy enforcement that makes replay refuse private workloads.

The Intelligence Loop

Where somm_recommend's evidence comes from: shadow evaluation, model-intelligence refresh, and the recommendation agent.

Engineering Reference

Package layout, CI gates, and the release machinery behind the six-package workspace these integrations ship in.