😏
This commit is contained in:
+389
@@ -0,0 +1,389 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
- Stabilized the pre-release 2.4.0 implementation and qualification evidence in
|
||||||
|
place. These review corrections do not add a Soma environment variable or package
|
||||||
|
dependency, and 2.3.0 remains the unchanged rollback. Evaluator-only provenance
|
||||||
|
arguments and llama.cpp server flags are not runtime configuration additions.
|
||||||
|
- Replaced isolated latest-turn rewriting with a bounded full-task context. Transform
|
||||||
|
calls can receive the original role-preserving `system`, `developer`, `user`,
|
||||||
|
`assistant`, and `tool` messages; tool results and definitions; `tool_choice`;
|
||||||
|
`parallel_tool_calls`; response-format, modality, audio, and stop controls; the
|
||||||
|
immutable target tool calls; and a clearly separated failed assistant draft.
|
||||||
|
Model and sampling knobs, transport headers, endpoint credentials, and rejected
|
||||||
|
transform candidates remain excluded. System/developer instructions remain
|
||||||
|
authoritative below Soma's JSON and native-tool invariants.
|
||||||
|
- Replaced independent whole-field regeneration with one fixed joint-repair object
|
||||||
|
containing required nullable `reasoning` and `content` members. Soma classifies all
|
||||||
|
present fields before repair and requests values only for fields marked for repair,
|
||||||
|
allowing accepted reasoning to ground content and jointly generated reasoning to
|
||||||
|
precede jointly generated content.
|
||||||
|
- Isolated each classification envelope to the named draft field while retaining the
|
||||||
|
complete original request, controls, and immutable calls. Repair generation may
|
||||||
|
inspect fields it must replace, but integrity verification sees only retained draft
|
||||||
|
fields and the current candidate, preventing rejected sibling text from influencing
|
||||||
|
either decision.
|
||||||
|
- Specialized the existing repair schema per call so requested members are strings
|
||||||
|
and every other required member is null. Added local JSON syntax/top-level checks
|
||||||
|
and literal stop-sequence checks for both target and repaired output, a fixed
|
||||||
|
alternate focus for candidate three, and bounded privacy-safe candidate rejection
|
||||||
|
reasons. Exact nonblank punctuation, symbol, and empty-container outputs are not
|
||||||
|
rejected by an English-text heuristic. These corrections add no JSON-schema
|
||||||
|
dependency or temperature branch.
|
||||||
|
- Added explicit field outcomes. A rejected optional reasoning field is dropped when
|
||||||
|
usable content remains; a reasoning-only response without content or a native tool
|
||||||
|
call is not accepted as terminal success. Immutable native `tool_calls` survive
|
||||||
|
exhausted adjacent-prose repair, with only the unusable prose cleared. Returned
|
||||||
|
function calls must match supplied definitions, and multiple calls are rejected
|
||||||
|
when the request sets `parallel_tool_calls=false`.
|
||||||
|
- Defined the normal semantic route as primary reasoning-off classification,
|
||||||
|
candidate one, and integrity verification, followed—after a completed semantic
|
||||||
|
rejection—by up to two fresh secondary reasoning-on candidates from the original
|
||||||
|
context. Each secondary candidate is verified on secondary. Without a secondary,
|
||||||
|
recovery is limited to two primary candidates. Availability failover remains
|
||||||
|
request-local. Configuration now enforces primary `off` and secondary `on` so the
|
||||||
|
deployed route cannot silently differ from the route the evaluator qualifies.
|
||||||
|
- Added a reasoning-off verifier fallback for a reasoning-on verification result that
|
||||||
|
is truncated or structurally invalid. The identical candidate is checked once on
|
||||||
|
the same endpoint/model with thinking disabled; Soma regenerates only after a
|
||||||
|
completed verifier returns `rewrite`. Semantic repairs never receive a rejected
|
||||||
|
candidate or prior-failure prose; structural JSON recovery receives only a closed
|
||||||
|
reason. Any `finish_reason=length` result is treated as truncated even when its
|
||||||
|
partial text happens to be syntactically valid JSON.
|
||||||
|
- Added per-endpoint `TRANSFORM_MEDIA_MODE=placeholder|forward|reject` and
|
||||||
|
`TRANSFORM_SECONDARY_MEDIA_MODE`. Placeholder mode preserves typed part positions
|
||||||
|
and metadata while omitting binary payload; forward mode sends the original media
|
||||||
|
as native OpenAI multimodal parts; reject mode requires a compatible alternate
|
||||||
|
route or fails explicitly. A forward-capability rejection is never silently
|
||||||
|
downgraded to placeholder mode. Malformed scalar content parts are rejected before
|
||||||
|
the target call, and streamed assistant-audio data/transcripts are accumulated
|
||||||
|
rather than overwritten. Mixed text/tool plus audio turns preserve their audio;
|
||||||
|
audio-only target turns fail explicitly because the text repair contract cannot
|
||||||
|
inspect or repair that payload.
|
||||||
|
- Added `TRANSFORM_ALLOW_CLARIFICATION`, default `false`. Missing task inputs no
|
||||||
|
longer license arbitrary examples, placeholders, invented results, or generic
|
||||||
|
promises; a clarification is an acceptable repair only when the operator opts in.
|
||||||
|
- Added the opt-in `TARGET_RETRY_ON_UNREPAIRABLE`, default `false`. When the complete
|
||||||
|
target turn leaves neither usable content nor a native tool call, Soma may make
|
||||||
|
exactly one fresh target request with a fixed corrective system message inserted
|
||||||
|
after the leading system/developer block. It preserves the original conversation
|
||||||
|
and tool contract and never includes rejected target or transform output. Optional
|
||||||
|
failed reasoning does not trigger this retry when content remains usable.
|
||||||
|
- Added the opt-in `TARGET_LOOP_BACK_ON_VERIFIED_REPAIR`, default `false`. When an
|
||||||
|
integrity-verified repair rewrites the target's refusal reasoning, Soma may make
|
||||||
|
exactly one additional target request consisting of the original conversation plus
|
||||||
|
a single 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 second response is processed under the same
|
||||||
|
bounded rules and never loops again; genuine refusals never loop because they
|
||||||
|
classify as `pass`. Loop-back and `TARGET_RETRY_ON_UNREPAIRABLE` remain mutually
|
||||||
|
exclusive per request under the shared two-target-call ceiling and aggregate
|
||||||
|
transform deadline. Backends that reject `reasoning_content` in input messages
|
||||||
|
fail explicitly.
|
||||||
|
- Raised the defaults to `TRANSFORM_CONTEXT_MAX_CHARS=131072`,
|
||||||
|
`TRANSFORM_FIELD_MAX_CHARS=32768`, `TRANSFORM_DECISION_MAX_TOKENS=1536`,
|
||||||
|
`TRANSFORM_REWRITE_MAX_TOKENS=16384`, and
|
||||||
|
`TRANSFORM_TOTAL_TIMEOUT=1200`. Semantic context is never silently truncated; the
|
||||||
|
configured context limit is capped at 4000000 characters and the field limit must
|
||||||
|
not exceed it. The limit is enforced after each phase removes discarded draft
|
||||||
|
fields, so oversized optional reasoning cannot block an otherwise bounded content
|
||||||
|
repair.
|
||||||
|
- Removed `TRANSFORM_CONFIRM_REWRITES`. Any presence of the obsolete variable now
|
||||||
|
fails configuration validation so a stale profile cannot appear to enable behavior
|
||||||
|
that no longer exists.
|
||||||
|
- Set hard ceilings of two target calls, 20 transform calls per target response, and
|
||||||
|
40 transform calls for one client request. Default configuration still makes one
|
||||||
|
target call because target retry is disabled. Fail-open no longer permits a
|
||||||
|
reasoning-only or otherwise empty terminal response.
|
||||||
|
- Expanded privacy-safe diagnostics, offline coverage, and live qualification around
|
||||||
|
full context, media routes, joint field policy, semantic candidate routing,
|
||||||
|
verifier fallback, and optional target retry. The frozen evaluator now covers 240
|
||||||
|
classification cases, 80 balanced joint message repairs, all three media modes,
|
||||||
|
staged primary-off/secondary-on routing, retained/forbidden facts, schema-off and
|
||||||
|
repeat sentinels, and reproducibility. Live target smoke remains explicitly opt-in.
|
||||||
|
Soma 2.4.0 ships unqualified until the exact transform profile passes every
|
||||||
|
automated gate; the report retains hash-bound evidence for all 80 repairs, and the
|
||||||
|
evaluator never downloads, loads, switches, starts, stops, or restarts a model.
|
||||||
|
Error responses after dispatch now carry the same privacy-safe counters as success
|
||||||
|
responses, target elapsed time includes malformed target responses, and exact
|
||||||
|
qualification records a dedicated secondary reasoning-budget value.
|
||||||
|
- The live evaluator can record llama.cpp's unrestricted reasoning-budget sentinel
|
||||||
|
as `--reasoning-budget -1` for an honest exploratory run. This never satisfies the
|
||||||
|
positive bounded-budget qualification gate; it only prevents current deployments
|
||||||
|
from being mislabeled as budget `0` or `512` while collecting repair evidence.
|
||||||
|
- Defined the first qualifying local route as temperature zero with a bounded
|
||||||
|
llama.cpp `--reasoning-budget 512`, recorded by the evaluator with the matching
|
||||||
|
`--reasoning-budget 512`. Temperature zero removes intentional sampling variance
|
||||||
|
from repeat gates but is not a guarantee of byte-identical output; other
|
||||||
|
temperatures and budgets remain separate profiles that need their own reports.
|
||||||
|
- Made the automated gates the sole qualification stage. A complete pass records
|
||||||
|
`qualified: true` and `qualification_status: qualified`; a failed gate records an
|
||||||
|
unqualified profile. Hash-bound retained outputs remain audit evidence and do not
|
||||||
|
form a separate approval stage.
|
||||||
|
- Added an explicit provider-managed evaluator profile for hosted-model experiments.
|
||||||
|
It records a retained, hash-bound public model metadata record and non-secret
|
||||||
|
endpoint/model identities, rejects fabricated local GGUF or reasoning-budget
|
||||||
|
provenance, and is always marked exploratory rather than qualified. Target smoke
|
||||||
|
now requires the exact requested `OK` token instead of accepting arbitrary
|
||||||
|
non-empty content.
|
||||||
|
- Added an explicitly unqualified hybrid evaluator profile for a local GGUF primary
|
||||||
|
and provider-managed secondary. It retains both provenance records without
|
||||||
|
pretending the hosted reasoning budget is known. Target-smoke requests now allow
|
||||||
|
128 output tokens while still requiring the response to be exactly `OK`.
|
||||||
|
- Kept the unrestricted-budget Qwen3.5-9B Q6_K report unqualified until a complete
|
||||||
|
replacement run exists. In-place evaluator stabilization changes its source/corpus
|
||||||
|
evidence identity, so the evaluator atomically installs a fresh temperature-zero,
|
||||||
|
512-token report before the obsolete unrestricted artifact is removed; the old JSON
|
||||||
|
is never edited or promoted.
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
|
||||||
|
- Replaced exact excerpt extraction, Unicode-equivalence recovery, unique-anchor
|
||||||
|
resolution, bounded fragment generation, masked retries, and local splicing with a
|
||||||
|
decision-only `pass | rewrite` classifier and complete-field regeneration. The
|
||||||
|
simpler contract removes the highest-fragility requirements for small transform
|
||||||
|
models while giving every model enough user context to produce a substantive
|
||||||
|
answer rather than a generic bridge.
|
||||||
|
- Added mandatory post-rewrite classification. Every locally valid candidate must
|
||||||
|
independently classify as `pass`; a residual or paraphrased refusal,
|
||||||
|
nonresponsive bridge, malformed verification result, or verification failure
|
||||||
|
rejects the candidate.
|
||||||
|
- Added one bounded fresh-regeneration path from the pristine source after a
|
||||||
|
received-output or post-check failure. A configured secondary performs the second
|
||||||
|
candidate when available; otherwise primary is reused. Rejected candidate text is
|
||||||
|
never fed back to either model, and the final candidate is verified exactly once.
|
||||||
|
- Added `TRANSFORM_REWRITE_MAX_TOKENS`, defaulting to 4096 with a valid range of
|
||||||
|
256–16384. Complete replacements are bounded after decoding to
|
||||||
|
`min(16,384, max(4,096, 2 * source characters))`; the rewrite schema deliberately
|
||||||
|
omits a dynamic `maxLength` for structured-output compatibility.
|
||||||
|
- Added explicit 16,384-character rewrite-input limits without semantic truncation.
|
||||||
|
Missing textual user context and oversized source/user inputs are deterministic,
|
||||||
|
field-scoped failures. Mixed-media turns carry an `unseen_media` flag; pure
|
||||||
|
non-text turns cannot be regenerated.
|
||||||
|
- Limited transform context to the complete field, latest textual user turn,
|
||||||
|
`tool_turn`, `unseen_media`, the deterministic output bound, and an optional closed
|
||||||
|
retry reason. Earlier messages, system instructions, processed reasoning, tools and
|
||||||
|
arguments, headers, credentials, and rejected candidates remain excluded.
|
||||||
|
- Raised the per-completion transform-call ceiling from eight to twelve to cover two
|
||||||
|
complete `(regeneration + post-check)` candidate paths for each reasoning/content
|
||||||
|
field while retaining at most two classification votes, the aggregate transform
|
||||||
|
deadline, one target call, and request-local backend stickiness.
|
||||||
|
- Removed the DSML parser, shim, counters, diagnostics, and documentation. Soma now
|
||||||
|
preserves native OpenAI `tool_calls` only; proprietary text-to-tool conversion is
|
||||||
|
owned by the target's OpenAI-compatible gateway. Native calls now require a
|
||||||
|
nonblank unique ID and `type: function`, preventing stream/non-stream envelope
|
||||||
|
drift and uncorrelatable tool results.
|
||||||
|
- Tightened the post-`[DONE]` SSE extension to at most one object with `choices: []`,
|
||||||
|
no keys outside `choices`, `cost`, and `usage`, and at least one non-null metadata
|
||||||
|
value. The postlude may end at EOF or one closing `[DONE]`; all later data is
|
||||||
|
rejected, and upstream error objects retain the redacted stream-error path.
|
||||||
|
Normalized outbound SSE emits one accepted `cost`/`usage` metadata event instead
|
||||||
|
of repeating one-shot cost data on every delta.
|
||||||
|
- Added `X-Soma-Postcheck-Rejections` and `classify`, `rewrite`, and `postcheck`
|
||||||
|
phase diagnostics with candidate/backend/reasoning/purpose metadata. Removed
|
||||||
|
rewrite-mode and DSML diagnostics while retaining strict privacy-safe logging,
|
||||||
|
field decisions, repair counts, rejected-rewrite counts, and fail-open visibility.
|
||||||
|
- Reworked live qualification around the exact imported 2.3.0 runtime, decision-only
|
||||||
|
classification, complete-field replacements, and verified final output. Local
|
||||||
|
reports belong under excluded `qualification-local/`; Soma never downloads,
|
||||||
|
starts, stops, switches, or otherwise manages a model.
|
||||||
|
- Distinguished automated-gate success from final qualification: reports remain
|
||||||
|
`pending_manual_review` until the 40 retained source/replacement pairs receive
|
||||||
|
independent semantic review. Exact-pair runs with a configured secondary inject
|
||||||
|
one labeled primary-unavailable event and require real secondary classification,
|
||||||
|
regeneration, and post-check calls; reports include per-backend phase counts.
|
||||||
|
- Made live provenance fail closed if either the imported Soma source or evaluator
|
||||||
|
changes during a run, preventing a report from hashing different code than it
|
||||||
|
actually exercised.
|
||||||
|
|
||||||
|
## 2.2.7
|
||||||
|
|
||||||
|
- Fixed the 2.2.6 streaming regression that rejected OpenAI-compatible gateway
|
||||||
|
usage or billing postludes emitted after an upstream `[DONE]`. Empty-choice
|
||||||
|
metadata is now preserved, including an optional final `[DONE]`, while premature
|
||||||
|
delimiters, malformed postludes, and any post-delimiter choice data remain invalid.
|
||||||
|
- Added explicit `off`, `on`, and `default` transform reasoning modes. `off`
|
||||||
|
preserves the 2.2.6 request shape, `on` uses llama.cpp's
|
||||||
|
`chat_template_kwargs.enable_thinking=true`, and `default` leaves reasoning
|
||||||
|
controls to the server. The setting applies consistently to classification and
|
||||||
|
rewriting.
|
||||||
|
- Added one optional, fully explicit secondary transform endpoint/model profile. It
|
||||||
|
may point to the primary server with a different reasoning mode or to an
|
||||||
|
independently operated server/model, but it shares Soma's prompt, temperature,
|
||||||
|
JSON contract, validation, and fixed generation bounds.
|
||||||
|
- Kept recovery bounded to the existing second attempt. The secondary can replace a
|
||||||
|
failed primary attempt after connection/timeouts, HTTP 408/429/5xx, invalid outer
|
||||||
|
completions, or locally rejected classification/rewrite output. Configuration
|
||||||
|
errors and other non-transient 4xx responses remain visible.
|
||||||
|
- Added optional secondary confirmation for valid primary `rewrite` classifications.
|
||||||
|
A secondary `pass` conservatively preserves the field; two valid `rewrite`
|
||||||
|
decisions retain the primary anchor. Confirmation is disabled by default and does
|
||||||
|
not add a third classification attempt.
|
||||||
|
- Added request-local failover: after a transient primary availability failure, only
|
||||||
|
the remaining transform phases in that Soma request begin on secondary. The next
|
||||||
|
client request starts on primary, so Soma retains no cross-request circuit-breaker
|
||||||
|
or harness/session state.
|
||||||
|
- Added a 600-second aggregate transform budget, starting after the single target
|
||||||
|
call, so retries and multi-field processing cannot multiply the per-call timeout
|
||||||
|
without bound. The one-target-call invariant and eight-transform-call ceiling are
|
||||||
|
unchanged.
|
||||||
|
- Extended safe diagnostics with backend, reasoning mode, call purpose, secondary
|
||||||
|
call/failover/disagreement counts, and transform-deadline failures without logging
|
||||||
|
prompts, generated text, anchors, credentials, or raw upstream request IDs.
|
||||||
|
- Extended live qualification to exercise explicit reasoning modes and composed
|
||||||
|
primary/secondary behavior without managing models. Deployment guidance requires
|
||||||
|
a secondary to qualify alone and the exact pair to qualify before an operator
|
||||||
|
enables confirmation; the evaluator does not change runtime configuration.
|
||||||
|
- An exploratory test of the operator-selected Qwen3.5-9B Q6_K with reasoning enabled
|
||||||
|
at temperature 0 removed the two reasoning-off false rewrites but regressed overall
|
||||||
|
refusal recall from 99% to 96%, introduced four invalid/missed partial-refusal
|
||||||
|
anchors and ten repeat failures, and remained unqualified. Targeted 64/128/256-token
|
||||||
|
reasoning budgets were non-monotonic, so Soma adds no model-specific
|
||||||
|
reasoning-budget knob.
|
||||||
|
|
||||||
|
## 2.2.6
|
||||||
|
|
||||||
|
- Preserved the 2.2.5 one-target-call flow, phase prompts, bounded transform call
|
||||||
|
counts, anchored/masked rewrite behavior, DSML normalization, `FAIL_OPEN` policy,
|
||||||
|
endpoints, and runtime environment variables while simplifying the implementation.
|
||||||
|
- Removed unused transform compatibility helpers, prefiltered protected
|
||||||
|
`ENABLE_REASONING` fields before overlay, reduced target-response mutation to one
|
||||||
|
working deep copy, removed custom HTTP pool sizing, and consolidated rewrite
|
||||||
|
contract validation under its owning parser/semantic validator.
|
||||||
|
- Made client flag validation exact: `stream` and `parallel_tool_calls` must be
|
||||||
|
booleans, while `n` is null or integer `1` and no longer accepts a boolean.
|
||||||
|
- Added target assistant-message validation before DSML or transform calls, including
|
||||||
|
role/content types, one non-empty reasoning alias, native tool-call shape,
|
||||||
|
non-blank function names, JSON-string arguments, and unusable empty turns. Unknown
|
||||||
|
valid target metadata remains preserved.
|
||||||
|
- Limited latest-user transform context to text: plain strings and the textual parts
|
||||||
|
of multimodal arrays are joined, while image, audio, file, base64, tool, and other
|
||||||
|
non-text metadata is omitted.
|
||||||
|
- Required refusal anchors to contain at least one letter or number and begin and end
|
||||||
|
on source boundaries; rewrites must likewise contain a letter or number.
|
||||||
|
Context-copy checks now ignore only splice-edge whitespace, closing the
|
||||||
|
copied-neighbor bypass without changing the local exact-splice behavior.
|
||||||
|
- Replaced generic streamed-response merging with explicit OpenAI delta assembly.
|
||||||
|
Text and every tool-call fragment append in arrival order, including identical
|
||||||
|
fragments; stable metadata is checked explicitly and unknown metadata uses
|
||||||
|
last-value semantics. Cumulative snapshot providers are explicitly unsupported.
|
||||||
|
- Require a terminal non-null SSE `finish_reason`; reject `[DONE]` or EOF before it,
|
||||||
|
meaningful deltas after it, malformed event JSON, and non-finite values. Normalized
|
||||||
|
outbound SSE remains unchanged.
|
||||||
|
- Reject non-finite configuration numbers, malformed endpoint ports, endpoint
|
||||||
|
userinfo/query/fragment components, duplicate or framing-sensitive configured
|
||||||
|
headers, duplicate transform-contract object members, and non-finite JSON across
|
||||||
|
client, target, transform, SSE, and DSML boundaries. Wire JSON now uses a strict,
|
||||||
|
surrogate-safe encoder.
|
||||||
|
- Tightened HTTP request framing to reject simultaneous `Content-Length` and
|
||||||
|
`Transfer-Encoding`, non-sole chunked encodings, negative chunks, and unterminated
|
||||||
|
chunk framing. No in-process body-size or buffering cap was added; those limits
|
||||||
|
remain the responsibility of a trusted front proxy.
|
||||||
|
- Preserved leading and trailing whitespace for DSML `string=true` arguments while
|
||||||
|
continuing to trim non-string JSON arguments. Sanitized upstream request IDs before
|
||||||
|
reflection and prevented upstream headers from colliding with Soma diagnostics.
|
||||||
|
- Reduced client-facing detail for streamed target/internal failures while retaining
|
||||||
|
server-side diagnostics, and removed query strings and upstream error text from
|
||||||
|
routine proxy-error logs.
|
||||||
|
- Added an opt-in standard-library live qualification evaluator with 200 reviewed
|
||||||
|
classifier fixtures, 40 category-balanced rewrite fixtures, schema-off sentinels,
|
||||||
|
deterministic parallel/repeat gates, reproducibility metadata, synthetic rewrite
|
||||||
|
output for human audit, and an optional 50-case one-shot target gate. Its reviewed
|
||||||
|
anchors must cover the curated refusal core, its JSON/channel selection mirrors the
|
||||||
|
runtime, and reports fingerprint the exact prompt/source/configuration snapshot.
|
||||||
|
It never downloads, switches, loads, or restarts a model.
|
||||||
|
- Recorded the tested aggressive Qwen3.5-9B Q8_0 deployment as unqualified after
|
||||||
|
false-positive and rewrite-stability failures. The official Qwen3.5-9B Q8_0 remains
|
||||||
|
a candidate that must independently pass the same gate; Soma performs no model
|
||||||
|
management.
|
||||||
|
- Recorded the operator-selected DavidAU Qwen3.5-9B Q6_K artifact as reproducibly
|
||||||
|
unqualified at temperature 0, seed 1, the temperature sampler, and one server slot;
|
||||||
|
preserved its exact machine-readable qualification report with the release. A
|
||||||
|
per-request temperature-0.7 comparison was also preserved and was substantially
|
||||||
|
worse, with 43 false rewrites and 93% refusal recall. Temperature 0 remains the
|
||||||
|
better tested setting, but neither configuration qualifies.
|
||||||
|
- Added two copyable deployment profiles using only existing variables, with one Soma
|
||||||
|
process and distinct listener port per harness. Documented isolated profile startup,
|
||||||
|
the trusted-local/reverse-proxy boundary, front-proxy resource limits, and the
|
||||||
|
non-loopback warning. No runtime harness branching was introduced.
|
||||||
|
|
||||||
|
## 2.2.5
|
||||||
|
|
||||||
|
- Fixed the observed 2.2.4 semantic-echo failure in which classification recovered to a valid literal anchor, but both rewrite attempts returned complete, schema-valid, `finish_reason=stop` JSON whose replacement was unchanged from that anchor; this was not a JSON-mode, truncation, or token-budget failure.
|
||||||
|
- Split the built-in classification and rewrite guidance so classification remains observational while the editor is explicitly permitted to produce a bounded non-refusal fragment without independently solving the user request.
|
||||||
|
- Strengthened classification recovery guidance so a corrected excerpt must remain a unique, exact, syntactically replaceable refusal clause containing the withholding decision rather than merely any literal source text.
|
||||||
|
- Kept the initial rewrite and ordinary repairs anchored, but masked `refusal_anchor` from the sole second attempt after an `unchanged` or `anchor_retained` result. The masked request carries only the field, bounded contexts, `anchor_omitted: true`, and the closed failure reason; the exact anchor and offsets remain local for validation and splicing.
|
||||||
|
- Added field-specific masked guidance that relies on the local splice to preserve both contexts and forbids copying, paraphrasing, summarizing, or otherwise reproducing them in the replacement.
|
||||||
|
- Made the field fallback instruction unconditional on every `masked_anchor` retry after a live Qwen run ignored a buried conditional fallback and copied immutable context. The final system instruction, appended after the masked contract, directs the transform to return exactly `{"replacement":"Proceed with the requested work."}` for reasoning or `{"replacement":"I'll proceed with the requested task."}` for content. This gives the semantic retry a fixed, unambiguous target while preserving normal server-output parsing, local validation, candidate-exhaustion, and `FAIL_OPEN` behavior; the shared schema and validator still accept any otherwise valid replacement fragment.
|
||||||
|
- Added privacy-safe `rewrite_mode=anchored|masked_anchor` diagnostics without logging the anchor, replacement, target text, user context, or rejected output; non-rewrite phases use `none`.
|
||||||
|
- Preserved the 64-character anchor cap, 512-character contexts, 256-character replacement cap, 384-token rewrite budget, two rewrite attempts, one target call, and eight-transform-call ceiling. Rewriting still receives no latest user turn, unbounded target field, or processed reasoning, and still has no complete-field fallback.
|
||||||
|
- Preserved `FAIL_OPEN` behavior: strict mode returns the exhausted transform error, while fail-open mode preserves only the affected field, marks it `failed_open`, and continues.
|
||||||
|
|
||||||
|
## 2.2.4
|
||||||
|
|
||||||
|
- Fixed the observed 2.2.3 failure in which classification JSON was valid, an initial complete-field rewrite retained the refusal, and its repair exhausted the token budget before closing its JSON object; JSON mode was enabled but could not guarantee completion of the unbounded complete-field edit.
|
||||||
|
- Replaced complete-field refusal rewriting with a deterministic local splice: classification must resolve a unique source anchor of at most 64 decoded characters, and the transform receives only that anchor plus at most 512 source characters on each side.
|
||||||
|
- Require the rewrite model to return only an anchor replacement of at most 256 decoded characters, with the fixed cap expressed in the llama.cpp-compatible JSON schema, repeated in the prompt, and enforced authoritatively in Python.
|
||||||
|
- Reject and repair complete-field candidates that repeat immutable context across a splice boundary or embed a substantial boundary segment inside wrapper text.
|
||||||
|
- Bound rewrite generation to 384 tokens and preserve the original prefix and suffix exactly; only the bounded source window is sent, never the latest user turn or processed reasoning, and there is no complete-field rewrite fallback.
|
||||||
|
- Apply one failure-specific repair to any received but unusable rewrite candidate, without echoing candidate text or exception details into the retry.
|
||||||
|
- Make exhausted rewrite candidates consistently honor `FAIL_OPEN`: strict mode returns 502 with the specific transform failure, while fail-open mode preserves only the affected field and marks it `failed_open`.
|
||||||
|
- Retained all endpoints, environment variables, response headers, client formats, the one-target-call invariant, and the eight-transform-call maximum.
|
||||||
|
- Documented that fixed small `maxLength` constraints work with the tested llama.cpp structured-output path, while previously considered large dynamic limits did not; because the schema is not model-visible, prompts also state each cap and local validation remains authoritative.
|
||||||
|
|
||||||
|
## 2.2.3
|
||||||
|
|
||||||
|
- Reduced classification excerpts from 128 to 64 decoded characters and directed the classifier to choose a compact, normally 2–8-word refusal anchor.
|
||||||
|
- Required exact contiguous provenance after JSON decoding, preserving case, Unicode punctuation, CRLF and other line endings, tabs, repeated spaces, and non-breaking spaces.
|
||||||
|
- Resolve only unique CRLF, no-break-space, or curly-quote normalization matches back to the exact original source slice with a streaming, memory-bounded matcher before rewriting; ambiguous matches, lone-CR changes, case changes, omissions, and general whitespace changes remain invalid.
|
||||||
|
- Clarified that llama.cpp JSON grammar constrains structure and string length but cannot enforce the dynamic rule that an excerpt occurs in the supplied source text; Soma continues to enforce that relationship locally.
|
||||||
|
- Gave the existing single classification recovery call failure-specific guidance using a closed failure reason, without echoing rejected transform output into the retry.
|
||||||
|
- Added privacy-safe diagnostic failure categories for invalid JSON or contract shape, pass-with-excerpt, empty excerpt, excessive length, and nonliteral excerpts; source text, excerpts, and transform output remain excluded from logs.
|
||||||
|
- Preserved the public `invalid_transform_output` error code, JSON-mode default and behavior when disabled, endpoints, environment variables, response headers, field-scoped `FAIL_OPEN` semantics, 768-token classification budget, and existing target and transform call bounds.
|
||||||
|
|
||||||
|
## 2.2.2
|
||||||
|
|
||||||
|
- Simplified classification to the strict `pass | rewrite` contract and removed the `safety` outcome.
|
||||||
|
- Route every refusal or withholding decision, including decisions formerly classified as safety boundaries, through the existing context-isolated rewrite stage.
|
||||||
|
- Continue using the latest user turn only for classification; rewriting still receives only the target field, refusal excerpt, and optional processed reasoning.
|
||||||
|
- Removed the safety-preserved counter, response header, and field-decision value from current diagnostics.
|
||||||
|
- Updated transform call bounds: `pass` stops field processing, while every refusal or withholding classification invokes the bounded rewrite path.
|
||||||
|
|
||||||
|
## 2.2.1
|
||||||
|
|
||||||
|
- Recover once from empty, truncated, malformed, or contract-invalid classification output before applying the configured field-scoped `FAIL_OPEN` policy.
|
||||||
|
- Preserve transform completion metadata and report incomplete classification generation as `transform_output_truncated` instead of an undifferentiated invalid-JSON error.
|
||||||
|
- Bound classification output to 768 tokens and require `rewrite` and `safety` excerpts to be literal, nonempty substrings no longer than 128 characters; `pass` requires an empty excerpt.
|
||||||
|
- Added `X-Soma-Classification-Retries` and safe classification-recovery diagnostics without logging transform output or request content.
|
||||||
|
- Treat decoded empty, missing, non-string, or structurally invalid rewrite replacements as `rewrite_rejected` while retaining operational handling for malformed JSON.
|
||||||
|
- Require literal equality for reasoning/content duplicate routing so case- or whitespace-normalized variants cannot replace the original field.
|
||||||
|
- Reject short and medium processed-reasoning copies in rewritten content using normalized equality, bidirectional containment, and bounded similarity checks.
|
||||||
|
- Documented classification and rewrite call bounds and clarified that JSON mode is enabled by default, requires a process restart after environment changes, and can be verified with `--check-config`, startup diagnostics, or `/health`.
|
||||||
|
|
||||||
|
## 2.2.0
|
||||||
|
|
||||||
|
- Split transform processing into contextual classification and context-isolated rewriting.
|
||||||
|
- Restored the exact latest user turn only for classification; user context never reaches rewriting.
|
||||||
|
- Added `pass`, `rewrite`, and `safety` classification outcomes.
|
||||||
|
- Preserve legitimate safety boundaries instead of treating every refusal as editable.
|
||||||
|
- Require an exact refusal excerpt for rewrite decisions.
|
||||||
|
- Added one bounded rewrite-only repair when an edit is unchanged or retains the classified excerpt.
|
||||||
|
- Removed strict reasoning compression and lexical-similarity floors; retained catastrophic growth and reasoning-copy protections.
|
||||||
|
- Process duplicate final text as content and duplicate tool-turn text as reasoning.
|
||||||
|
- Added detected-refusal, safety-preserved, and rewrite-repair diagnostics.
|
||||||
|
- Retained stateless operation, one target call, native tool-call fidelity, DSML normalization, buffered SSE, and field-scoped fail-open behavior.
|
||||||
|
|
||||||
|
## 2.1.2
|
||||||
|
|
||||||
|
- Rebalanced refusal handling with more sensitive classification and conservative edits.
|
||||||
|
- Added independent field processing and `rewrite_rejected` diagnostics.
|
||||||
|
|
||||||
|
## 2.1.1
|
||||||
|
|
||||||
|
- Ignored unnecessary replacement text when `refusal:false`.
|
||||||
|
- Simplified llama.cpp JSON grammar to avoid nested `maxLength` failures.
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
- Removed user context from transforms, separated reasoning/content guidance, added structured JSON, rewrite bounds, and duplicate handling.
|
||||||
@@ -0,0 +1,740 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
*
|
||||||
|
!*.env.example
|
||||||
|
!.gitignore
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Soma deployment profile A. Copy to harness-a.env and edit endpoint values.
|
||||||
|
PROXY_HOST=127.0.0.1
|
||||||
|
PROXY_PORT=8080
|
||||||
|
unset TRANSFORM_CONFIRM_REWRITES
|
||||||
|
|
||||||
|
TARGET_URL=http://127.0.0.1:9001/v1
|
||||||
|
TARGET_KEY=
|
||||||
|
TARGET_HEADERS_JSON={}
|
||||||
|
|
||||||
|
# Candidate 1/classification/integrity route: deterministic reasoning off.
|
||||||
|
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
|
||||||
|
# Fresh semantic repair candidates 2/3: reasoning on. This example intentionally
|
||||||
|
# reuses the same process and therefore provides no availability isolation.
|
||||||
|
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
|
||||||
|
|
||||||
|
ENABLE_REASONING={}
|
||||||
|
TRANSFORM_TEMPERATURE=0
|
||||||
|
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_JSON_MODE=true
|
||||||
|
# Opt-in only after qualifying a second target response and its cost/latency.
|
||||||
|
TARGET_RETRY_ON_UNREPAIRABLE=false
|
||||||
|
FAIL_OPEN=false
|
||||||
|
FORWARD_CLIENT_HEADERS=true
|
||||||
|
REQUIRE_DISTINCT_ENDPOINTS=true
|
||||||
|
CONNECT_TIMEOUT=15
|
||||||
|
REQUEST_TIMEOUT=600
|
||||||
|
TRANSFORM_TOTAL_TIMEOUT=1200
|
||||||
|
UPSTREAM_ERROR_BODY_LIMIT=4000
|
||||||
|
SSE_CHUNK_CHARS=2048
|
||||||
|
LOG_LEVEL=INFO
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Soma deployment profile B. Copy to harness-b.env and edit endpoint values.
|
||||||
|
# Its listener and target differ from profile A; both may use the same qualified
|
||||||
|
# transform service if that service has enough independently tested capacity.
|
||||||
|
PROXY_HOST=127.0.0.1
|
||||||
|
PROXY_PORT=8081
|
||||||
|
unset TRANSFORM_CONFIRM_REWRITES
|
||||||
|
|
||||||
|
TARGET_URL=http://127.0.0.1:9002/v1
|
||||||
|
TARGET_KEY=
|
||||||
|
TARGET_HEADERS_JSON={}
|
||||||
|
|
||||||
|
# Candidate 1/classification/integrity route: deterministic reasoning off.
|
||||||
|
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
|
||||||
|
# Fresh semantic repair candidates 2/3: reasoning on. This example intentionally
|
||||||
|
# reuses the same process and therefore provides no availability isolation.
|
||||||
|
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
|
||||||
|
|
||||||
|
ENABLE_REASONING={}
|
||||||
|
TRANSFORM_TEMPERATURE=0
|
||||||
|
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_JSON_MODE=true
|
||||||
|
# Opt-in only after qualifying a second target response and its cost/latency.
|
||||||
|
TARGET_RETRY_ON_UNREPAIRABLE=false
|
||||||
|
FAIL_OPEN=false
|
||||||
|
FORWARD_CLIENT_HEADERS=true
|
||||||
|
REQUIRE_DISTINCT_ENDPOINTS=true
|
||||||
|
CONNECT_TIMEOUT=15
|
||||||
|
REQUEST_TIMEOUT=600
|
||||||
|
TRANSFORM_TOTAL_TIMEOUT=1200
|
||||||
|
UPSTREAM_ERROR_BODY_LIMIT=4000
|
||||||
|
SSE_CHUNK_CHARS=2048
|
||||||
|
LOG_LEVEL=INFO
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
requests>=2.31,<3
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Soma listener
|
||||||
|
# Keep Soma loopback-only unless a trusted reverse proxy supplies access control,
|
||||||
|
# rate limits, request-size limits, and TLS.
|
||||||
|
PROXY_HOST=127.0.0.1
|
||||||
|
PROXY_PORT=8080
|
||||||
|
|
||||||
|
# Removed in 2.4.0. Clear a value inherited from a shell that previously sourced a
|
||||||
|
# 2.3.x profile; Soma rejects even TRANSFORM_CONFIRM_REWRITES=false.
|
||||||
|
unset TRANSFORM_CONFIRM_REWRITES
|
||||||
|
|
||||||
|
# Target model endpoint
|
||||||
|
TARGET_URL=https://opencode.ai/zen/v1
|
||||||
|
TARGET_KEY=
|
||||||
|
TARGET_HEADERS_JSON={}
|
||||||
|
|
||||||
|
# Primary classifier, first repair candidate, and integrity verifier. The primary
|
||||||
|
# reasoning-off route is the fast deterministic path.
|
||||||
|
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
|
||||||
|
|
||||||
|
# Required semantic-repair route when a secondary is configured: use the same
|
||||||
|
# already-running model with
|
||||||
|
# reasoning enabled, or configure an independently operated endpoint/model. Soma
|
||||||
|
# sends fresh candidates from the original context; it never sends rejected output.
|
||||||
|
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
|
||||||
|
|
||||||
|
# Optional target-specific reasoning overlay
|
||||||
|
ENABLE_REASONING={}
|
||||||
|
# ENABLE_REASONING={"thinking":{"type":"enabled"}}
|
||||||
|
|
||||||
|
# Qualification uses zero to remove intentional sampling variance from repeat gates.
|
||||||
|
# Any nonzero value is a different profile and requires a new report.
|
||||||
|
TRANSFORM_TEMPERATURE=0
|
||||||
|
# Full transform task-context and individual target-field limits. Inputs are rejected,
|
||||||
|
# never silently truncated. TRANSFORM_FIELD_MAX_CHARS must not exceed the context cap.
|
||||||
|
TRANSFORM_CONTEXT_MAX_CHARS=131072
|
||||||
|
TRANSFORM_FIELD_MAX_CHARS=32768
|
||||||
|
# Decision/verifier and joint-repair output budgets.
|
||||||
|
TRANSFORM_DECISION_MAX_TOKENS=1536
|
||||||
|
TRANSFORM_REWRITE_MAX_TOKENS=16384
|
||||||
|
# A clarification is not accepted as a repaired answer unless explicitly enabled.
|
||||||
|
TRANSFORM_ALLOW_CLARIFICATION=false
|
||||||
|
# JSON-constrained transform output is enabled by default.
|
||||||
|
# Restart Soma after changing this value. Verify configuration before launch with
|
||||||
|
# `python3 soma.py --check-config`, then inspect the startup log or `/health` to
|
||||||
|
# confirm the effective value in the running process.
|
||||||
|
TRANSFORM_JSON_MODE=true
|
||||||
|
|
||||||
|
# One fresh target retry is available only when the complete target turn is
|
||||||
|
# unrepairable and leaves no usable content or native tool call. It is opt-in because
|
||||||
|
# it adds target latency/cost and can produce another model action.
|
||||||
|
TARGET_RETRY_ON_UNREPAIRABLE=false
|
||||||
|
|
||||||
|
# false: transform failures, including failed integrity checks and exhausted candidates, return 502
|
||||||
|
# true: preserve usable original refusing text after repair failure; reasoning-only
|
||||||
|
# or otherwise empty terminal turns still fail
|
||||||
|
FAIL_OPEN=false
|
||||||
|
|
||||||
|
FORWARD_CLIENT_HEADERS=true
|
||||||
|
# Same-server primary/secondary transform profiles are allowed; this protects only
|
||||||
|
# against the target sharing an origin with either transform endpoint.
|
||||||
|
REQUIRE_DISTINCT_ENDPOINTS=true
|
||||||
|
CONNECT_TIMEOUT=15
|
||||||
|
REQUEST_TIMEOUT=600
|
||||||
|
# One aggregate recovery budget after the first target response. It includes every
|
||||||
|
# transform call and an optional second target call plus processing of its response.
|
||||||
|
TRANSFORM_TOTAL_TIMEOUT=1200
|
||||||
|
UPSTREAM_ERROR_BODY_LIMIT=4000
|
||||||
|
SSE_CHUNK_CHARS=2048
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
# Optional common prefix added to classification, joint-repair, and integrity prompts.
|
||||||
|
# Leave unset for Soma's built-in 2.4.0 phase-specific transform behavior. A custom
|
||||||
|
# prefix materially changes the transform profile and requires separate qualification.
|
||||||
|
# TRANSFORM_PROMPT=...
|
||||||
+1020
File diff suppressed because it is too large
Load Diff
+3651
File diff suppressed because it is too large
Load Diff
+4436
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user