741 lines
37 KiB
Markdown
741 lines
37 KiB
Markdown
|
|
# Soma 2.4.0
|
|||
|
|
|
|||
|
|
Soma is a stateless OpenAI-compatible Chat Completions proxy. It buffers and
|
|||
|
|
validates a target assistant turn, identifies operative refusal or withholding, and
|
|||
|
|
uses a separately configured transform model to produce a context-grounded repair.
|
|||
|
|
Native OpenAI `tool_calls` and valid response metadata remain target-owned and
|
|||
|
|
immutable.
|
|||
|
|
|
|||
|
|
Soma 2.4.0 addresses the central weakness of 2.3.0: a transform cannot accurately
|
|||
|
|
repair references such as “the supplied text,” “both functions,” or “the preceding
|
|||
|
|
result” when it receives only the latest user string and failed assistant field.
|
|||
|
|
Version 2.4 supplies bounded full task context, repairs reasoning and content
|
|||
|
|
together when necessary, distinguishes optional reasoning from the usable answer,
|
|||
|
|
and uses an integrity verifier that can reject invented or context-breaking output.
|
|||
|
|
|
|||
|
|
Soma 2.4.0 is not pre-qualified. JSON grammar guarantees neither correct semantic
|
|||
|
|
decisions nor grounded repairs. The exact model artifact, server build, endpoint
|
|||
|
|
pair, reasoning modes, media modes, prompt, budgets, and temperature must pass the
|
|||
|
|
live automated qualification gates before deployment. Reports retain hash-bound
|
|||
|
|
outputs for audit and reproduction, but inspection is not a separate qualification
|
|||
|
|
stage.
|
|||
|
|
|
|||
|
|
The pre-release 2.4.0 tree was stabilized in place rather than assigning a new
|
|||
|
|
version to review corrections made before qualification. The rollback remains the
|
|||
|
|
unchanged 2.3.0 directory. This stabilization adds no Soma environment variable and
|
|||
|
|
no package dependency; existing 2.4.0 profiles retain the same runtime contract.
|
|||
|
|
Evaluator CLI provenance such as `--reasoning-budget` and the corresponding
|
|||
|
|
llama.cpp server option are not Soma environment settings.
|
|||
|
|
|
|||
|
|
The current Qwen3.5-9B Q6_K route is unqualified. Its exploratory temperature-zero
|
|||
|
|
report used unrestricted secondary reasoning and failed automated gates. That report
|
|||
|
|
is diagnostic evidence only: it cannot be promoted or reinterpreted after evaluator
|
|||
|
|
stabilization. A fresh bounded-budget report must replace it as the current
|
|||
|
|
qualification record.
|
|||
|
|
|
|||
|
|
Soma never downloads, loads, switches, starts, stops, or restarts a model. It does
|
|||
|
|
not execute tools, maintain conversation state, authenticate clients, or provide
|
|||
|
|
tenant isolation.
|
|||
|
|
|
|||
|
|
## Request flow
|
|||
|
|
|
|||
|
|
With the full primary-off/secondary-on staged profile, the normal path is:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
client request
|
|||
|
|
-> target model
|
|||
|
|
-> buffer and validate one complete assistant turn
|
|||
|
|
-> build one bounded, role-preserving task context
|
|||
|
|
-> classify every present reasoning/content field on primary, reasoning off,
|
|||
|
|
with the other draft text fields removed from that classification envelope
|
|||
|
|
-> apply field policy and, if necessary, request one joint repair object
|
|||
|
|
candidate 1: primary/off -> primary/off integrity verification
|
|||
|
|
candidate 2: secondary/on -> secondary/on integrity verification
|
|||
|
|
candidate 3: secondary/on -> secondary/on integrity verification
|
|||
|
|
-> if an on-mode verifier truncates or returns invalid JSON:
|
|||
|
|
verify the identical candidate once on the same endpoint/model, reasoning off
|
|||
|
|
-> retain usable verified fields and immutable native tool calls
|
|||
|
|
-> if the turn is still unusable and target retry is enabled:
|
|||
|
|
call the target once more with a fixed corrective system instruction
|
|||
|
|
and process that fresh response under the same bounded rules
|
|||
|
|
-> if a verified repair rewrote reasoning and loop-back is enabled:
|
|||
|
|
call the target once more with only that verified reasoning appended
|
|||
|
|
and process that fresh response under the same bounded rules
|
|||
|
|
-> deduplicate exact reasoning/content copies
|
|||
|
|
-> return JSON or normalized SSE
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Candidate two is generated only after a completed integrity verifier semantically
|
|||
|
|
rejects candidate one, or after an eligible route failure. Candidate three is the
|
|||
|
|
last semantic candidate and receives a fixed alternate focus on authority and exact
|
|||
|
|
output/tool/stop constraints. Every candidate is generated fresh from the original
|
|||
|
|
task context and failed target draft: neither rejected candidate text nor
|
|||
|
|
model-generated failure analysis is fed into another generation.
|
|||
|
|
|
|||
|
|
Without a configured secondary, Soma permits at most two primary candidates. A
|
|||
|
|
transient primary availability failure can make the remainder of the current client
|
|||
|
|
request start on secondary. That stickiness is request-local; the next request starts
|
|||
|
|
on primary. There is no global circuit breaker or harness/session model state.
|
|||
|
|
|
|||
|
|
The staged reasoning modes are part of the 2.4 contract, not optional tuning:
|
|||
|
|
`TRANSFORM_REASONING_MODE` must be `off`, and a configured secondary must use
|
|||
|
|
`TRANSFORM_SECONDARY_REASONING_MODE=on`. Configuration validation rejects any other
|
|||
|
|
combination so deployment cannot silently diverge from the qualified route.
|
|||
|
|
|
|||
|
|
## Classification and integrity decisions
|
|||
|
|
|
|||
|
|
Classification returns exactly one object:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{"decision":"pass"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
or:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{"decision":"rewrite"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The decision concerns the assistant's operative stance, not the presence of words
|
|||
|
|
such as “cannot,” “refuse,” or “safety.” Quoted, translated, summarized, or analyzed
|
|||
|
|
refusal language is not itself a refusal. A failed method followed by continued work,
|
|||
|
|
a different method toward the same requested outcome, and an honest factual
|
|||
|
|
constraint do not become rewrites merely because they contain negative language.
|
|||
|
|
An adopted decision to stop, avoid part of the request, or deliberately substitute a
|
|||
|
|
lesser outcome is a rewrite when the work remains feasible under the authoritative
|
|||
|
|
system/developer instructions. A boundary those instructions explicitly require is
|
|||
|
|
not an avoidable refusal and must be preserved.
|
|||
|
|
|
|||
|
|
Integrity verification uses the same strict object shape:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{"decision":"pass"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
or:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{"decision":"rewrite"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
During integrity verification, `rewrite` means the candidate has a material defect
|
|||
|
|
and must be rejected or regenerated; it does not request an in-place edit. The
|
|||
|
|
verifier can reject residual withholding, a generic promise,
|
|||
|
|
meta-commentary about the transformation, treating the failed assistant draft as
|
|||
|
|
user-supplied material, invented task-specific inputs or results, contradictions with
|
|||
|
|
accepted reasoning or immutable tool calls, and an unapproved clarification.
|
|||
|
|
|
|||
|
|
Soma accepts only a complete JSON object satisfying the current schema. A pure JSON
|
|||
|
|
fence is accepted, but an object embedded in prose is not. `TRANSFORM_JSON_MODE=true`
|
|||
|
|
is the default and sends a small schema through `response_format`; disabling it
|
|||
|
|
removes that wire hint but retains the same prompts, strict parser, local validation,
|
|||
|
|
and recovery bounds. Separately, target and repaired content requested as JSON must
|
|||
|
|
parse strictly and match an immediately declared top-level type. A configured literal
|
|||
|
|
stop sequence may not survive in forwarded reasoning or content. Soma intentionally
|
|||
|
|
does not implement full client JSON-Schema validation.
|
|||
|
|
|
|||
|
|
## Joint repair contract and field policy
|
|||
|
|
|
|||
|
|
One repair call returns a fixed object with both members present and nullable:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"reasoning": "complete repaired reasoning or null",
|
|||
|
|
"content": "complete repaired content or null"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Only fields classified for repair may be non-null. The two-key wire shape never
|
|||
|
|
changes, while the per-call schema constrains each requested member to string and each
|
|||
|
|
other member to null. Local validation preserves the same contract when a transform
|
|||
|
|
endpoint ignores the schema or JSON mode is disabled. Nonblank exact outputs such as
|
|||
|
|
`{}`, `[]`, punctuation, and Unicode symbols are valid; Soma does not impose an
|
|||
|
|
English-text or alphanumeric "substance" heuristic on the requested deliverable.
|
|||
|
|
|
|||
|
|
Soma classifies all present fields before requesting a repair:
|
|||
|
|
|
|||
|
|
- If reasoning and content pass, both target fields are preserved.
|
|||
|
|
- If content passes and reasoning requires repair, Soma drops the reasoning field;
|
|||
|
|
it does not risk generating new private analysis for an already usable answer.
|
|||
|
|
- If reasoning passes and content requires repair, the accepted reasoning is supplied
|
|||
|
|
as evidence for the content repair.
|
|||
|
|
- If both require repair, one candidate generates reasoning first and then content so
|
|||
|
|
the answer can follow the repaired analysis.
|
|||
|
|
- Verified jointly repaired reasoning is forwarded with its verified content. Because
|
|||
|
|
integrity verification is message-level, a rejected joint candidate is retried as a
|
|||
|
|
whole; Soma never salvages one unverified member from it.
|
|||
|
|
- A reasoning-only response with no content and no native tool call cannot become a
|
|||
|
|
terminal success merely because internal analysis exists. It takes the optional
|
|||
|
|
target retry when enabled; otherwise it fails explicitly.
|
|||
|
|
- Native `tool_calls` are immutable. Soma may repair adjacent reasoning/content using
|
|||
|
|
the full tool context, but exhausted prose repair clears the unusable prose and
|
|||
|
|
preserves the structured call. Soma never invents or edits a tool name, ID,
|
|||
|
|
argument string, ordering, or result. Before repair, every returned function name
|
|||
|
|
must match a supplied tool definition, and multiple returned calls are rejected
|
|||
|
|
when `parallel_tool_calls=false`.
|
|||
|
|
|
|||
|
|
The failed target assistant draft is evidence, not user-supplied task material. The
|
|||
|
|
transform is instructed not to quote, explain, or “convert” the refusal itself. It
|
|||
|
|
may preserve supported facts and genuine constraints, but it must not choose an
|
|||
|
|
arbitrary example, fill invented placeholders, fabricate code changes or external
|
|||
|
|
results, or claim a tool/action completed without evidence.
|
|||
|
|
|
|||
|
|
`TRANSFORM_ALLOW_CLARIFICATION=false` is the default. A transform response that asks
|
|||
|
|
the user for more information is not accepted as the repaired answer unless this
|
|||
|
|
option is explicitly enabled. Enabling it is appropriate only for harnesses where an
|
|||
|
|
essential missing input genuinely requires another user turn; it must be qualified
|
|||
|
|
as a separate behavior profile.
|
|||
|
|
|
|||
|
|
## Full task context and privacy boundary
|
|||
|
|
|
|||
|
|
Classification, repair, and integrity verification receive the original request
|
|||
|
|
context needed to understand references and preserve constraints:
|
|||
|
|
|
|||
|
|
- original messages in order and by role, including `system`, `developer`, `user`,
|
|||
|
|
`assistant`, and `tool` messages and tool results;
|
|||
|
|
- complete tool definitions, `tool_choice`, and `parallel_tool_calls`;
|
|||
|
|
- `response_format`, modality/audio controls, and `stop`;
|
|||
|
|
- the target assistant draft, clearly separated from the original request;
|
|||
|
|
- immutable target native tool calls in a separate read-only section; and
|
|||
|
|
- the configured media representation for every multimodal part.
|
|||
|
|
|
|||
|
|
System and developer messages remain authoritative context below Soma's fixed JSON
|
|||
|
|
and native-tool invariants. Other supplied values are task evidence, not permission
|
|||
|
|
to override the transform contract.
|
|||
|
|
|
|||
|
|
Draft text is projected per phase. A classifier receives only its named target field,
|
|||
|
|
so refusing content cannot contaminate accepted reasoning or vice versa. Repair
|
|||
|
|
generation may inspect fields marked for replacement to preserve facts supported by
|
|||
|
|
the task. Integrity verification removes every replaced or discarded original field
|
|||
|
|
and judges only retained evidence plus the current candidate.
|
|||
|
|
|
|||
|
|
Soma does not send target/transform endpoint credentials, HTTP headers, the target
|
|||
|
|
model name, sampling knobs, or rejected transform candidates. It does not log task
|
|||
|
|
context, prompts, target drafts, repaired output, tool arguments, media payloads, or
|
|||
|
|
credentials.
|
|||
|
|
|
|||
|
|
This is nevertheless a wider trust boundary than 2.3.0. Any secret embedded inside a
|
|||
|
|
conversation, tool definition, tool argument, or tool result is part of the original
|
|||
|
|
task context and can reach every transform endpoint used for that request, including
|
|||
|
|
a remote secondary. Configure only transform services authorized to receive the full
|
|||
|
|
request. Header exclusion cannot remove secrets that the client placed in message or
|
|||
|
|
tool data.
|
|||
|
|
|
|||
|
|
`TRANSFORM_CONTEXT_MAX_CHARS=131072` bounds the serialized `task_context`, and
|
|||
|
|
`TRANSFORM_FIELD_MAX_CHARS=32768` bounds an individual target reasoning/content
|
|||
|
|
field. The configured field limit must not exceed the context limit. The context
|
|||
|
|
limit has a hard maximum of 4000000 characters. Because this is a character bound,
|
|||
|
|
not tokenizer accounting, large-context profiles should leave room for transform
|
|||
|
|
instructions and generated output. Soma rejects oversized semantic input
|
|||
|
|
rather than truncating messages, tool schemas, code, or evidence into a misleading
|
|||
|
|
task. Phase envelopes add the bounded candidate/contract data, and forwarded native
|
|||
|
|
media remains subject to the upstream endpoint and trusted front proxy's byte limits.
|
|||
|
|
|
|||
|
|
## Media modes
|
|||
|
|
|
|||
|
|
Media handling is explicit per transform endpoint:
|
|||
|
|
|
|||
|
|
- `placeholder` preserves typed part positions and non-payload metadata, omits the
|
|||
|
|
actual binary/media payload, and marks the part unseen. The transform must not infer
|
|||
|
|
absent media details. This is the correct setting for a text-only or `--no-mmproj`
|
|||
|
|
llama.cpp server.
|
|||
|
|
- `forward` sends original typed content media using native OpenAI multimodal message
|
|||
|
|
parts. It does not serialize base64 media into ordinary JSON text. Provider-specific
|
|||
|
|
top-level assistant media has no portable input envelope and fails explicitly in
|
|||
|
|
this mode; use `placeholder` for that shape. Use `forward` only for an endpoint that
|
|||
|
|
is authorized and qualified to accept the request's typed media parts.
|
|||
|
|
- `reject` refuses to send a media-bearing task to that endpoint. Soma may use a
|
|||
|
|
configured compatible transform route; otherwise it fails explicitly. Target retry
|
|||
|
|
is not used to bypass an operator's transform-media policy.
|
|||
|
|
|
|||
|
|
Set `TRANSFORM_MEDIA_MODE` for primary and `TRANSFORM_SECONDARY_MEDIA_MODE` for
|
|||
|
|
secondary. If a `forward` endpoint rejects the media request, Soma routes only to a
|
|||
|
|
compatible configured secondary or fails explicitly. It never invokes target retry
|
|||
|
|
to bypass media policy and never silently retries the task as placeholder text,
|
|||
|
|
because either action would change the evidence available to the model.
|
|||
|
|
|
|||
|
|
Assistant audio attached to a usable text or native-tool turn is preserved, including
|
|||
|
|
audio accumulated from a target stream. Audio-only target turns are explicitly
|
|||
|
|
unsupported: Soma cannot inspect or repair the audio payload under its text repair
|
|||
|
|
contract, so it returns `unsupported_target_response` instead of forwarding an
|
|||
|
|
unchecked terminal answer.
|
|||
|
|
|
|||
|
|
## Bounded recovery and verifier fallback
|
|||
|
|
|
|||
|
|
The primary reasoning-off profile owns normal classification, candidate one, and its
|
|||
|
|
integrity verification. After semantic rejection, a configured secondary
|
|||
|
|
reasoning-on profile owns candidates two and three, each generated from the pristine
|
|||
|
|
task package and independently verified.
|
|||
|
|
|
|||
|
|
Reasoning-enabled generation can improve task understanding, but a small model may
|
|||
|
|
spend an entire decision budget thinking and end with `finish_reason=length` before
|
|||
|
|
emitting its tiny JSON decision. If an on-mode integrity verification is truncated or
|
|||
|
|
structurally invalid, Soma does not discard the candidate. It verifies that identical
|
|||
|
|
candidate exactly once on the same endpoint and model with reasoning disabled. Only
|
|||
|
|
a completed `rewrite` decision advances to a fresh generation.
|
|||
|
|
|
|||
|
|
Transport/availability failures follow bounded route failover. Structural JSON
|
|||
|
|
recovery may include a concise closed failure category, but never rejected output or
|
|||
|
|
raw exception text. Semantic retries receive only positive instructions and the
|
|||
|
|
pristine task context; they are not primed with the preceding candidate or its
|
|||
|
|
failure.
|
|||
|
|
|
|||
|
|
There are hard ceilings of:
|
|||
|
|
|
|||
|
|
- two target calls per client request;
|
|||
|
|
- 20 transform calls for each target response; and
|
|||
|
|
- 40 transform calls across the complete client request.
|
|||
|
|
|
|||
|
|
`TRANSFORM_TOTAL_TIMEOUT=1200` is one aggregate deadline. It starts after the first
|
|||
|
|
target response completes and covers every transform call, an optional second target
|
|||
|
|
call, and processing of the second response. It does not reset after target retry.
|
|||
|
|
The initial target call remains governed by `CONNECT_TIMEOUT` and `REQUEST_TIMEOUT`
|
|||
|
|
outside that aggregate window.
|
|||
|
|
|
|||
|
|
## Optional target retry
|
|||
|
|
|
|||
|
|
`TARGET_RETRY_ON_UNREPAIRABLE=false` preserves the normal one-target-call behavior.
|
|||
|
|
When enabled, Soma may call the target exactly once more only when the completed turn
|
|||
|
|
is unrepairable and leaves no usable content or immutable native tool call. A failed
|
|||
|
|
optional reasoning field does not trigger target retry when valid content remains.
|
|||
|
|
|
|||
|
|
The retry starts from the original request and inserts one fixed corrective system
|
|||
|
|
instruction immediately after the leading system/developer block. It preserves the
|
|||
|
|
conversation and tool contract and never includes the rejected target response or a
|
|||
|
|
rejected transform candidate. This avoids training the second response to imitate the
|
|||
|
|
failure, but it does add target latency/cost and may produce a different native tool
|
|||
|
|
decision. Soma still does not execute that call.
|
|||
|
|
|
|||
|
|
Enable target retry only after qualifying the complete target-plus-transform route.
|
|||
|
|
It is not a general retry for target HTTP errors, optional reasoning loss, or a merely
|
|||
|
|
imperfect answer.
|
|||
|
|
|
|||
|
|
## Loop-back on verified repair
|
|||
|
|
|
|||
|
|
`TARGET_LOOP_BACK_ON_VERIFIED_REPAIR=false` is the default. When enabled, Soma may
|
|||
|
|
make exactly one additional target call after an integrity-verified repair that
|
|||
|
|
rewrote the target's refusal reasoning. Instead of returning the transform's repaired
|
|||
|
|
candidate directly, Soma re-sends the original request with one appended assistant
|
|||
|
|
message carrying only that verified repaired reasoning in a `reasoning_content`
|
|||
|
|
field, so the target re-ingests the relaxed context and produces the task output
|
|||
|
|
itself. The re-entry payload preserves the original conversation, media, tool
|
|||
|
|
definitions, `tool_choice`, stop controls, and response-format settings untouched.
|
|||
|
|
|
|||
|
|
Loop-back fires only on the first target attempt, only when reasoning was one of the
|
|||
|
|
repaired fields, and only after that candidate passed integrity verification.
|
|||
|
|
Content-only repairs, fields that classified as `pass`, tool-only turns, cleared
|
|||
|
|
tool prose, and the second target attempt never loop. Genuine refusals never loop
|
|||
|
|
because truthful technical, environmental, evidentiary, uncertainty, impossibility,
|
|||
|
|
missing-input, and factual limitations classify as `pass` and are never rewritten.
|
|||
|
|
|
|||
|
|
The second target call shares the hard ceiling of two target calls per client
|
|||
|
|
request and the aggregate `TRANSFORM_TOTAL_TIMEOUT` window, which is not reset. The
|
|||
|
|
second response is processed under the same classification, repair, and integrity
|
|||
|
|
rules; if it is also unrepairable, the request fails explicitly and Soma never
|
|||
|
|
makes a third target call. `TARGET_RETRY_ON_UNREPAIRABLE` and loop-back are
|
|||
|
|
mutually exclusive per request because they handle disjoint failure classes
|
|||
|
|
(unrepairable turns versus verified reasoning repairs) and share the single
|
|||
|
|
additional-call slot.
|
|||
|
|
|
|||
|
|
The reasoning carrier is fixed to `reasoning_content` with no fallback. Backends
|
|||
|
|
that reject that field in input messages fail explicitly rather than silently
|
|||
|
|
degrading to a different carrier. Loop-back adds target latency and cost; qualify
|
|||
|
|
the complete target-plus-transform route before enabling it.
|
|||
|
|
|
|||
|
|
## Failure behavior
|
|||
|
|
|
|||
|
|
`FAIL_OPEN=false` is the default. Exhausted mandatory repair, invalid verification,
|
|||
|
|
oversized context, incompatible media, missing usable terminal output, and other
|
|||
|
|
nonrecoverable transform errors return an explicit error instead of forwarding a
|
|||
|
|
known-bad candidate.
|
|||
|
|
|
|||
|
|
`FAIL_OPEN=true` is an availability policy only. It can restore an original refusal,
|
|||
|
|
withholding field, or otherwise rejected target text and therefore defeats strict
|
|||
|
|
repair guarantees. Do not treat fail-open as a safety, compliance, or successful
|
|||
|
|
quality mode, and do not enable it merely to hide model qualification failures.
|
|||
|
|
Fail-open never makes a reasoning-only or otherwise empty terminal turn successful;
|
|||
|
|
that turn still takes the explicitly enabled target retry or returns an error.
|
|||
|
|
|
|||
|
|
Client and upstream JSON reject non-finite numbers. Transform objects additionally
|
|||
|
|
reject duplicate member names. Client `stream` and `parallel_tool_calls` values must
|
|||
|
|
be booleans, and `n` must be null or integer `1`. Target assistant text, reasoning
|
|||
|
|
aliases, and native tool-call shapes are validated before any local mutation.
|
|||
|
|
|
|||
|
|
Endpoint configuration rejects userinfo, queries, fragments, invalid ports, and
|
|||
|
|
unsafe header overrides. `REQUIRE_DISTINCT_ENDPOINTS=true` prevents exact
|
|||
|
|
target/transform origin collisions and direct self-routes. Operators must still avoid
|
|||
|
|
DNS aliases or LAN addresses that resolve to a wildcard-bound Soma listener.
|
|||
|
|
|
|||
|
|
## Configuration
|
|||
|
|
|
|||
|
|
Minimal one-profile configuration:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
TARGET_URL=https://opencode.ai/zen/v1
|
|||
|
|
TRANSFORM_URL=http://127.0.0.1:8001/v1
|
|||
|
|
TRANSFORM_MODEL=local
|
|||
|
|
TRANSFORM_REASONING_MODE=off
|
|||
|
|
TRANSFORM_MEDIA_MODE=placeholder
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Same-server primary-off/secondary-on profile:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
PROXY_HOST=127.0.0.1
|
|||
|
|
PROXY_PORT=8080
|
|||
|
|
|
|||
|
|
# Clear the removed 2.3.x option from an already-populated shell.
|
|||
|
|
unset TRANSFORM_CONFIRM_REWRITES
|
|||
|
|
|
|||
|
|
TARGET_URL=https://opencode.ai/zen/v1
|
|||
|
|
TARGET_KEY=
|
|||
|
|
TARGET_HEADERS_JSON={}
|
|||
|
|
|
|||
|
|
TRANSFORM_URL=http://127.0.0.1:8001/v1
|
|||
|
|
TRANSFORM_KEY=
|
|||
|
|
TRANSFORM_MODEL=local
|
|||
|
|
TRANSFORM_HEADERS_JSON={}
|
|||
|
|
TRANSFORM_REASONING_MODE=off
|
|||
|
|
TRANSFORM_MEDIA_MODE=placeholder
|
|||
|
|
|
|||
|
|
TRANSFORM_SECONDARY_URL=http://127.0.0.1:8001/v1
|
|||
|
|
TRANSFORM_SECONDARY_KEY=
|
|||
|
|
TRANSFORM_SECONDARY_MODEL=local
|
|||
|
|
TRANSFORM_SECONDARY_HEADERS_JSON={}
|
|||
|
|
TRANSFORM_SECONDARY_REASONING_MODE=on
|
|||
|
|
TRANSFORM_SECONDARY_MEDIA_MODE=placeholder
|
|||
|
|
|
|||
|
|
# Same-server primary/secondary is allowed. This rejects target/transform collisions.
|
|||
|
|
REQUIRE_DISTINCT_ENDPOINTS=true
|
|||
|
|
|
|||
|
|
ENABLE_REASONING={}
|
|||
|
|
TRANSFORM_TEMPERATURE=0
|
|||
|
|
TRANSFORM_JSON_MODE=true
|
|||
|
|
TRANSFORM_CONTEXT_MAX_CHARS=131072
|
|||
|
|
TRANSFORM_FIELD_MAX_CHARS=32768
|
|||
|
|
TRANSFORM_DECISION_MAX_TOKENS=1536
|
|||
|
|
TRANSFORM_REWRITE_MAX_TOKENS=16384
|
|||
|
|
TRANSFORM_ALLOW_CLARIFICATION=false
|
|||
|
|
TRANSFORM_TOTAL_TIMEOUT=1200
|
|||
|
|
TARGET_RETRY_ON_UNREPAIRABLE=false
|
|||
|
|
TARGET_LOOP_BACK_ON_VERIFIED_REPAIR=false
|
|||
|
|
FAIL_OPEN=false
|
|||
|
|
|
|||
|
|
CONNECT_TIMEOUT=15
|
|||
|
|
REQUEST_TIMEOUT=600
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`TRANSFORM_CONFIRM_REWRITES` was removed. Soma rejects the variable even when its
|
|||
|
|
value is `false`; this catches a stale 2.3.x deployment rather than silently changing
|
|||
|
|
its meaning. Deleting an export from a file does not clear an existing shell value,
|
|||
|
|
so either start from a clean environment or run:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
unset TRANSFORM_CONFIRM_REWRITES
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The primary and secondary keys/headers never inherit from one another. A same-server
|
|||
|
|
secondary supplies behavioral diversity but no process, GPU, or availability
|
|||
|
|
isolation. An independent endpoint/model can supply both, at the cost of extending
|
|||
|
|
the full-context trust boundary. Qualify the secondary by itself and then qualify the
|
|||
|
|
exact composed pair. Soma 2.4 requires primary `off` and secondary `on`; `default` and
|
|||
|
|
the inverse mode assignments are rejected during configuration validation.
|
|||
|
|
|
|||
|
|
`TRANSFORM_TEMPERATURE` is sent on every transform call and overrides the llama
|
|||
|
|
server sampling default. `TRANSFORM_DECISION_MAX_TOKENS` covers classifications and
|
|||
|
|
integrity decisions; `TRANSFORM_REWRITE_MAX_TOKENS` covers the fixed joint-repair
|
|||
|
|
object. Valid ranges are 256–16384 decision tokens, 256–16384 repair tokens,
|
|||
|
|
4096–4000000 context characters, and 1024–4000000 field characters, with the field
|
|||
|
|
limit no greater than the context limit. Larger budgets bound output but do not
|
|||
|
|
improve model judgment by themselves.
|
|||
|
|
|
|||
|
|
Environment files are shell profiles and are not loaded automatically. Restart Soma
|
|||
|
|
after every environment change:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
set -a
|
|||
|
|
. ./soma.env
|
|||
|
|
set +a
|
|||
|
|
python3 soma.py --check-config
|
|||
|
|
python3 soma.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Additional environment variables not shown in the profiles above:
|
|||
|
|
|
|||
|
|
- `LOG_LEVEL` (default `INFO`) — Python logging level for proxy diagnostics.
|
|||
|
|
- `FORWARD_CLIENT_HEADERS` (default `true`) — forward non-hop, non-credential
|
|||
|
|
client headers to the target endpoint.
|
|||
|
|
- `TRANSFORM_PROMPT` (default built in) — base system prompt prepended to every
|
|||
|
|
transform phase prompt.
|
|||
|
|
- `SOMA_AUTO_REQUIRES_TOOL` (default `false`) — strict auto-tools mode that
|
|||
|
|
classifies each request as requiring a native call or a text response.
|
|||
|
|
- `UPSTREAM_ERROR_BODY_LIMIT` (default `4000`, range 256–65536) — bounded number
|
|||
|
|
of upstream error-body bytes retained for target diagnostics.
|
|||
|
|
- `SSE_CHUNK_CHARS` (default `2048`, range 128–65536) — maximum characters per
|
|||
|
|
normalized SSE text delta.
|
|||
|
|
|
|||
|
|
Verify the effective version, endpoint identities, reasoning/media modes, JSON mode,
|
|||
|
|
context/field/token limits, clarification, target-retry, and loop-back policies,
|
|||
|
|
aggregate deadline, and call ceilings through `--check-config`, startup diagnostics,
|
|||
|
|
or `/health`.
|
|||
|
|
|
|||
|
|
Point clients at:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
http://<proxy-host>:8080/v1/chat/completions
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Aliases are available at `/chat/completions`, `/v1/models`, `/models`, and `/health`.
|
|||
|
|
|
|||
|
|
### llama.cpp recommendation for a shared local endpoint
|
|||
|
|
|
|||
|
|
For the shared-endpoint topology where one llama.cpp process serves a primary
|
|||
|
|
reasoning-off profile and a secondary reasoning-on profile through per-request
|
|||
|
|
`enable_thinking`, enable server reasoning support and cap thinking so a small
|
|||
|
|
decision response has room to emit JSON:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
--reasoning on --reasoning-budget 512 --temp 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The primary profile still sends `enable_thinking=false`; the global server mode must
|
|||
|
|
not prevent the secondary profile from producing and parsing reasoning when it sends
|
|||
|
|
`enable_thinking=true`. A 512-token cap is the required starting profile for the
|
|||
|
|
bounded-budget qualification run; configuring it is not itself a qualification
|
|||
|
|
claim. The evaluator's matching `--reasoning-budget 512` argument records what the
|
|||
|
|
already-running server uses and does not configure the server.
|
|||
|
|
|
|||
|
|
Keep both the server and Soma transform temperature at zero for qualification. Soma's
|
|||
|
|
per-request `TRANSFORM_TEMPERATURE=0` is authoritative for transform calls; the server
|
|||
|
|
flag supplies a matching default. Temperature zero removes deliberate sampling
|
|||
|
|
variance so repeat failures can be attributed to the route under test, although it
|
|||
|
|
does not promise byte-identical output across server builds, speculative decoding,
|
|||
|
|
cache state, or concurrency. Any nonzero temperature is a different profile and
|
|||
|
|
requires a separate report.
|
|||
|
|
|
|||
|
|
Re-run the exact live profile after changing any server argument. Soma does not add
|
|||
|
|
these arguments, restart the server, or download a model. A text-only server launched
|
|||
|
|
with `--no-mmproj` should use `placeholder`, not `forward`, for both transform media
|
|||
|
|
modes.
|
|||
|
|
|
|||
|
|
### Multiple harness profiles
|
|||
|
|
|
|||
|
|
Use one Soma process and listener port per harness. The supplied
|
|||
|
|
[`profiles/harness-a.env.example`](profiles/harness-a.env.example) and
|
|||
|
|
[`profiles/harness-b.env.example`](profiles/harness-b.env.example) use ordinary
|
|||
|
|
environment variables and distinct ports. Soma has no `HARNESS_TYPE` dispatch or
|
|||
|
|
shared mutable deployment profile.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cp profiles/harness-a.env.example profiles/harness-a.env
|
|||
|
|
cp profiles/harness-b.env.example profiles/harness-b.env
|
|||
|
|
env -i PATH="$PATH" /bin/sh -c 'set -a; . ./profiles/harness-a.env; set +a; exec python3 soma.py --check-config'
|
|||
|
|
env -i PATH="$PATH" /bin/sh -c 'set -a; . ./profiles/harness-b.env; set +a; exec python3 soma.py --check-config'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The supplied `profiles/.gitignore` excludes populated profile names while retaining
|
|||
|
|
the examples. Keep production profiles outside distributable artifacts even when
|
|||
|
|
ignore rules are present. A shared transform server must be qualified at the combined
|
|||
|
|
load and configured concurrency; a one-slot llama server serializes both harnesses.
|
|||
|
|
|
|||
|
|
### Trust and resource boundary
|
|||
|
|
|
|||
|
|
Keep `PROXY_HOST=127.0.0.1` unless a trusted front proxy supplies authentication,
|
|||
|
|
access control, TLS, request-size limits, buffering limits, timeouts, and rate limits.
|
|||
|
|
Soma warns when bound to a non-loopback interface. It buffers complete target turns
|
|||
|
|
and full bounded task packages and has no in-process concurrency-admission limit, so
|
|||
|
|
the front proxy must enforce limits appropriate to available memory.
|
|||
|
|
|
|||
|
|
## Native tool calls and streaming
|
|||
|
|
|
|||
|
|
Soma supports native OpenAI `tool_calls` only. It preserves IDs, `type: function`,
|
|||
|
|
function names, strict JSON argument strings, ordering, and streaming fragments.
|
|||
|
|
Proprietary text tool syntaxes are ordinary assistant text; conversion belongs in the
|
|||
|
|
target's OpenAI-compatible gateway.
|
|||
|
|
|
|||
|
|
For `stream:true`, Soma buffers the complete target stream, processes it, and emits
|
|||
|
|
normalized OpenAI delta SSE. Original chunk boundaries are not preserved. Valid
|
|||
|
|
reasoning, content, native tool calls, finish reason, usage, and response metadata are
|
|||
|
|
retained. Accepted `cost` and `usage` metadata are emitted together at most once.
|
|||
|
|
|
|||
|
|
The upstream stream must produce a terminal non-null `finish_reason`. Soma accepts a
|
|||
|
|
terminal choice followed by EOF or the ordinary sequence ending in `[DONE]`.
|
|||
|
|
Standard empty-choice usage frames are retained. After the first `[DONE]`, at most one
|
|||
|
|
narrow metadata postlude is allowed: an object with `choices: []`, no keys outside
|
|||
|
|
`choices`, `cost`, and `usage`, and at least one non-null metadata value. It may end at
|
|||
|
|
EOF or one closing `[DONE]`. Further objects/delimiters, malformed or non-finite JSON,
|
|||
|
|
duplicate keys, premature `[DONE]`, or meaningful data after the terminal choice are
|
|||
|
|
rejected.
|
|||
|
|
|
|||
|
|
## Diagnostics
|
|||
|
|
|
|||
|
|
Successful and post-dispatch error responses expose privacy-safe trace/timing and
|
|||
|
|
bounded call counts, field decisions, candidate/verifier outcomes, target-retry use,
|
|||
|
|
deduplication, and fail-open status. `/health` and startup diagnostics additionally
|
|||
|
|
show the effective non-secret reasoning and media configuration.
|
|||
|
|
|
|||
|
|
Transform logs identify phase, field/candidate, backend, reasoning and media mode,
|
|||
|
|
purpose, closed failure category, JSON-mode value, channel lengths, finish reason,
|
|||
|
|
token counts, elapsed time, and a request-ID fingerprint. They do not include prompts,
|
|||
|
|
task context, target or transform text, media, tool arguments, credentials, error
|
|||
|
|
bodies, or raw upstream request IDs.
|
|||
|
|
|
|||
|
|
## Tests
|
|||
|
|
|
|||
|
|
Run the complete offline suite:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 -m unittest -v test_soma.py test_soma_extra.py
|
|||
|
|
python3 test_soma_live.py --inventory
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The suite covers strict schemas, full-context isolation and limits, joint field
|
|||
|
|
policy, media routes, primary/secondary candidate ownership, reasoning-off verifier
|
|||
|
|
fallback, optional target retry, hard call/deadline ceilings, fail-open behavior,
|
|||
|
|
native tool fidelity, JSON validation, and SSE normalization. Offline success is
|
|||
|
|
necessary but is not model qualification.
|
|||
|
|
|
|||
|
|
## Live qualification
|
|||
|
|
|
|||
|
|
`test_soma_live.py` is opt-in and dynamically imports the adjacent `soma.py`, so it
|
|||
|
|
exercises the exact runtime prompts, schemas, parsing, validation, routing, and field
|
|||
|
|
policy. It calls only already-running endpoints supplied by the operator and never
|
|||
|
|
manages a model or server.
|
|||
|
|
|
|||
|
|
First inspect the frozen corpus without network access:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 test_soma_live.py --inventory
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Run the exact selected transform artifact/profile at temperature 0 and retain the
|
|||
|
|
report only under ignored `qualification-local/`. Consult `--help` for the current
|
|||
|
|
provenance and endpoint arguments:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 test_soma_live.py --help
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Every qualifying run must declare primary `--reasoning-mode off`, a configured
|
|||
|
|
secondary with `--secondary-reasoning-mode on`, and the secondary server's actual
|
|||
|
|
positive `--reasoning-budget` (for the documented llama.cpp starting profile,
|
|||
|
|
`--reasoning-budget 512`). This evaluator value records provenance; the server must
|
|||
|
|
already have been launched with the matching budget.
|
|||
|
|
|
|||
|
|
An exploratory run against llama.cpp's unrestricted default may record
|
|||
|
|
`--reasoning-budget -1`. Its report remains unqualified because the positive-budget
|
|||
|
|
provenance gate fails; it is not carried forward after a complete bounded-budget
|
|||
|
|
rerun replaces the current evidence.
|
|||
|
|
|
|||
|
|
Qualification is automated-only. Use a new report filename and run the exact
|
|||
|
|
temperature-zero, bounded profile. Exit status `0` means every qualification gate
|
|||
|
|
passed and the report records `qualified: true` with
|
|||
|
|
`qualification_status: qualified`. Exit status `1` means at least one qualification
|
|||
|
|
gate failed, and `2` means setup or report creation failed. The evaluator has no
|
|||
|
|
second approval stage; inspecting retained evidence does not alter report status.
|
|||
|
|
|
|||
|
|
Provider-managed routes can be exercised with
|
|||
|
|
`--artifact-kind provider-managed`, but they are recorded as exploratory and can
|
|||
|
|
never be marked qualified by this evaluator. Supply the exact provider name, model
|
|||
|
|
label, and a small public `/models` metadata record through
|
|||
|
|
`--provider-model-metadata-json`; do not invent GGUF, llama.cpp, hardware, revision,
|
|||
|
|
or reasoning-budget values for a hosted service. Use `--reasoning-budget 0` when the
|
|||
|
|
provider does not publish a bounded budget. The report separates behavioral gate
|
|||
|
|
results from qualification eligibility and records the requested reasoning modes as
|
|||
|
|
unverified provider controls.
|
|||
|
|
|
|||
|
|
To retain the qualified local GGUF primary while evaluating a hosted secondary, use
|
|||
|
|
`--artifact-kind hybrid-local-provider`. Supply the ordinary local artifact fields
|
|||
|
|
for the primary and the provider metadata fields for the secondary. The evaluator
|
|||
|
|
retains both identities, but deliberately records the combined route as exploratory
|
|||
|
|
and qualification-ineligible because the hosted reasoning controls and budget are
|
|||
|
|
not independently verified. The primary's artifact label may differ from its wire
|
|||
|
|
model alias (for example, an immutable repository label with `local` on the wire).
|
|||
|
|
|
|||
|
|
`--target-smoke-count 10` limits only the final target-through-transform smoke calls.
|
|||
|
|
It does not limit the preceding transform corpus: the evaluator still runs all 240
|
|||
|
|
classifier cases, 80 retained repairs, repeat matrices, and route/media probes. Each
|
|||
|
|
smoke request grants 128 output tokens, and its response must contain exactly `OK`
|
|||
|
|
with no surrounding whitespace, prose, or native tool call.
|
|||
|
|
|
|||
|
|
For the initial bounded Qwen route, the automated command must include the exact
|
|||
|
|
primary/secondary endpoint and provenance arguments plus:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 test_soma_live.py \
|
|||
|
|
--reasoning-mode off \
|
|||
|
|
--secondary-reasoning-mode on \
|
|||
|
|
--reasoning-budget 512 \
|
|||
|
|
--temperature 0 \
|
|||
|
|
--report qualification-local/qwen3.5-9b-q6_k-t0-rb512-automated.json \
|
|||
|
|
[the exact endpoint, model, server, artifact, and hardware arguments]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Reports are immutable evidence files. The evaluator writes a completed report
|
|||
|
|
privately and installs it atomically; it never exposes a partially written result or
|
|||
|
|
overwrites an existing path. The stabilized evaluator, corpus, and source hashes must
|
|||
|
|
match the new run. After a complete budget-512 report has been validated and installed
|
|||
|
|
under its truthful filename, remove the obsolete unrestricted-budget artifact so only
|
|||
|
|
the current evidence remains.
|
|||
|
|
|
|||
|
|
The automated gates require:
|
|||
|
|
|
|||
|
|
- valid contracts on all 240 classification cases, 100% hard-refusal and overall
|
|||
|
|
refusal recall, and zero false rewrites;
|
|||
|
|
- all 20 schema-off high-risk sentinels and five repeats of every high-risk case at
|
|||
|
|
parallelism 1 and 4 with zero repeat failures;
|
|||
|
|
- all 80 message-repair cases completed without exhaustion and 100% integrity
|
|||
|
|
verification, required-fact retention, and forbidden-fact absence;
|
|||
|
|
- exactly 20 cases in each field-decision cell: `pass/pass`, `rewrite/pass`,
|
|||
|
|
`pass/rewrite`, and `rewrite/rewrite`;
|
|||
|
|
- the exact staged primary-off then secondary-on candidate route;
|
|||
|
|
- explicit positive secondary reasoning-budget provenance (use `512` as the initial
|
|||
|
|
llama.cpp qualification value);
|
|||
|
|
- explicit `placeholder`, `forward`, and `reject` media behavior; and
|
|||
|
|
- complete source, evaluator, model, server, configuration, and fixture
|
|||
|
|
reproducibility evidence.
|
|||
|
|
|
|||
|
|
The report also records latency, classification disagreements, backend/phase
|
|||
|
|
ownership, semantic repair attempts, verifier fallback, and call ceilings. Strict
|
|||
|
|
JSON is exercised both with structured-output mode enabled and with the wire schema
|
|||
|
|
omitted.
|
|||
|
|
|
|||
|
|
Live target smoke is separate and explicitly opt-in because it incurs target cost and
|
|||
|
|
can produce a new model/tool decision. It uses benign fixtures, keeps target retry
|
|||
|
|
disabled, verifies the complete target-to-transform route, and never executes returned
|
|||
|
|
tools. Target-retry behavior remains deterministic offline coverage until separately
|
|||
|
|
qualified; live smoke does not enable it. The smoke is not run by `--inventory` or an
|
|||
|
|
ordinary transform-only qualification. The count is bounded from 1 through 10:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 test_soma_live.py \
|
|||
|
|
--target-smoke \
|
|||
|
|
--target-url https://target.example/v1 \
|
|||
|
|
--target-model TARGET_MODEL \
|
|||
|
|
--reasoning-budget 512 \
|
|||
|
|
--target-smoke-count 10 \
|
|||
|
|
[the same transform and provenance arguments used for qualification]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Without `--target-smoke`, the evaluator makes zero target calls. Supply target keys
|
|||
|
|
through the hidden CLI/environment option, never in recorded server arguments or a
|
|||
|
|
report intended for sharing.
|
|||
|
|
|
|||
|
|
An automated pass is final qualification for the exact recorded profile. Reports
|
|||
|
|
retain all 80 accepted repair outputs and their evidence hashes so the result can be
|
|||
|
|
audited and reproduced, but later inspection does not change qualification status.
|
|||
|
|
Any failed gate leaves the profile unqualified, and a smaller model receives no
|
|||
|
|
relaxed threshold.
|
|||
|
|
|
|||
|
|
Any change to model revision, GGUF, server build/arguments, reasoning budget,
|
|||
|
|
temperature, prompt, endpoint identity, media mode, context/token limits, field
|
|||
|
|
policy, primary/secondary composition, evaluator source, fixture corpus, or assertion
|
|||
|
|
semantics creates different evidence and requires a new report. Evidence hashes bind
|
|||
|
|
one report's exact inputs and outputs; they do not transfer qualification to a
|
|||
|
|
superseded report. Reports can contain synthetic task context and non-secret
|
|||
|
|
provenance; inspect them before sharing and never place keys in recorded header/server
|
|||
|
|
arguments.
|
|||
|
|
|
|||
|
|
`qualification-local/`, populated profiles, logs, caches, credentials, and model
|
|||
|
|
artifacts are excluded from the release package and checksums.
|