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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_TIMEOUT_SECONDS=30
# POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_MODEL_SETTINGS={"dimensions":2560}
# Fixed-dimension models reject the dimensions request field. Keep EMBEDDING_DIMENSION and disable sending:
# POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_SEND_DIMENSIONS=false

# Optional Atomic Memory extraction over a same-host gateway's cached turn prefix (off unless the URL is set).
# Requires GENERATION_MODEL, which normal extraction uses as the fallback. Headers are secrets.
# POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_URL=http://127.0.0.1:8787
# POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_HEADERS={"Authorization":"Bearer replace-me"}
# POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_CACHED_INPUT_RATIO=0.1
# A dedicated reranker LLM is optional; without it, reranking reuses the generation model.
# POWERCONTEXT_SERVER_INFERENCE_RERANK_MODEL=openai-chat:local-reranker
# POWERCONTEXT_SERVER_INFERENCE_RERANK_BASE_URL=http://127.0.0.1:8082/v1
Expand Down
28 changes: 25 additions & 3 deletions docs/en/development/capture-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Do not add content, paths, message ID lists, prompts, model configuration or cre
| Field | Contract |
| --- | --- |
| `schema` | Exactly `powercontext.capture-coverage.v1`. |
| `projection` | One of the three profiles below. |
| `projection` | One of the four profiles below. |
| `unit` | The exact unit paired with that profile. |
| `status` | `complete`, `partial` or `unknown`. |
| `reasons` | Unique supported strings in ascending lexicographic order. |
Expand All @@ -65,7 +65,8 @@ interpreted as unknown. Interpretation never rewrites the stored payload.
| --- | --- | --- |
| `hermes-turn-text.v1` | `user_assistant_text_pair` | The current normalized user/assistant text pair with the existing role labels and separators. |
| `hermes-precompress-text.v1` | `new_user_assistant_window` | The new user/assistant entries delivered by the host after existing filtering and overlap removal. |
| `gateway-turn-text.v1` | `gateway_turn_projection` | The existing supported Chat/Responses normalized turn from the last user message through the terminal assistant response. |
| `gateway-turn-text.v1` | `gateway_turn_projection` | The earlier Chat/Responses normalized turn from the last user message through the terminal assistant response, with byte limits per field and per record. Used only to interpret existing Sources. |
| `gateway-turn-text.v2` | `gateway_turn_projection` | The complete turn the Gateway now writes, over the same range as v1, with nothing truncated or omitted. |

### Hermes completed turn

Expand All @@ -90,7 +91,28 @@ window before sending a partial Source or advancing the captured snapshot.
Scope binding, overlap identities and checkpoint transitions retain their existing semantics.
Once capture is durable, later extraction failure does not invalidate the checkpoint.

### Gateway normalized turn
### Gateway complete turn (v2)

New Gateway Sources use `gateway-turn-text.v2`. It keeps everything the model read in the turn: the user text, other
messages after it, visible reasoning summaries, every argument field of each tool call, tool outputs and errors,
results returned by built-in tools, unrecognized items, and the final assistant text. No field is truncated and no
step is omitted. Injected prepare content, provider reasoning ciphertext, IDs, statuses and other transport metadata
are outside the profile, and `data:` binary content becomes a placeholder with its media type and size. Excluding
them does not itself make this projection partial.

So that Core's normal extraction can process each Source on its own (default input budget 24,000 estimated tokens),
a turn over 12,000 tokens by Core's character estimate (of the escaped text: one token per 4 ASCII characters or per
other character) is split, in order, into several Sources. Each part is a
complete JSON document with `part` and `parts`. A single step that does not fit has its longest text field split into
consecutive steps of the same type, and the later pieces carry `"continued": true`. Splitting loses nothing, so every
part declares `complete`. The metadata `turn` of each part gives the turn's `source_id` and the part number. Terminal
response detection is the same as in v1.

### Gateway normalized turn (v1)

The following describes the v1 projection, for interpreting existing Sources that declare it; the Gateway no longer
writes v1.


The projection retains its existing visible text, tool calls and tool outputs. Injected prepare
content, opaque reasoning, transport IDs and unsupported media remain outside the profile.
Expand Down
3 changes: 3 additions & 0 deletions docs/en/docs/operate/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ still occupies an HTTP slot until it finishes; reserve other slots for concurren
| `POWERCONTEXT_SERVER_INFERENCE_GENERATION_STREAMING` | `disabled` | When `auto`, only generation models whose provider prefix is `anthropic`, `openai`, `openai-chat`, or `openai-responses` use a streamed request aggregated locally into one complete response. Other prefixes stay on non-streaming requests. `disabled` keeps every model on non-streaming requests |
| `POWERCONTEXT_SERVER_INFERENCE_GENERATION_STREAM_IDLE_TIMEOUT_SECONDS` | `60` | Maximum idle seconds while aggregating a streamed request: while the stream opens and waits for its first model event, and between model events (text, thinking, or tool-call parts). Provider heartbeat frames do not reset this timer; a longer gap fails as a timeout |
| `POWERCONTEXT_SERVER_INFERENCE_GENERATION_MODEL_CONTEXT_WINDOW_TOKENS` | `125000` | Total generation-model context window used to budget Topic processing |
| `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_URL` | unset | Base URL of a gateway on the same host; extracts Atomic Memory for a single gateway turn over its cached prefix and falls back to normal extraction. Requires `GENERATION_MODEL`. See [Memory extraction over the cached prefix](deploy-server.md#memory-extraction-over-the-cached-prefix) |
| `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_CACHED_INPUT_RATIO` | `0.1` | Price of a cached input token relative to an uncached one for the agent's target, greater than 0 and at most 1; the prefix is reused only when it is estimated to cost less than normal extraction |
| `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_HEADERS` | `{}` | JSON object of static headers for the gateway, such as its `Authorization` key; values are secrets |
| `POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_MODEL` | unset | Pydantic AI embedding model; requires profile ID and dimension |
| `POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_BASE_URL` | provider default | Custom OpenAI-compatible embeddings base URL |
| `POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_HEADERS` | `{}` | JSON object of static embedding client headers; values are secrets |
Expand Down
45 changes: 45 additions & 0 deletions docs/en/docs/operate/deploy-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,51 @@ These are per-body and per-class concurrency limits. They do not measure retaine
RSS ceiling. Process-wide retained-byte accounting and output-capacity discovery belong to the remaining
[G5-B work in #2464](https://github.com/oceanbase/powercontext/issues/2464).

### Memory extraction over the cached prefix

When the gateway runs on the same host as the Server, Atomic Memory extraction for a captured turn can reuse the
provider's prompt cache for the agent's conversation instead of sending the transcript again. It is off by default and
needs both sides:

- **Gateway:** `PC_GATEWAY_PREFIX_EXTRACTION=on` together with capture (`PC_GATEWAY_SEDIMENT=on`). For a Responses
conversation, the gateway keeps the exact request it sent and the response output of each turn's final response in
memory, for at most 30 minutes and at most 64 turns. It records an opaque handle in the captured Source's metadata,
with the input and output token counts of that final response.
The request and response never enter the Source, evidence, or logs. The captured Source records the turn in full
(projection `gateway-turn-text.v2`): no field is truncated and no step is omitted, and every argument field of a tool
call, tool outputs and errors, other messages, and unrecognized items are included. Only the provider's encrypted
reasoning, IDs, and other transport metadata are left out, and binary data becomes a placeholder with its size.
Injected memory blocks are not in the Source. Each Source must fit Core's default extraction input on its own, so
a turn over about 12,000 tokens by Core's character estimate is captured as consecutive Sources. Their metadata
`turn` names the turn and the part, and the handle is on the last part.
- **Server:** `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_URL` set to the gateway's listen address, and
`POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_HEADERS` set to `{"Authorization": "Bearer <PC_GATEWAY_API_KEY>"}`
when the gateway requires a key. A generation model is still required, because normal extraction is the fallback.

Cached input is not free: the provider still bills it at a reduced rate. When a processing window holds exactly one
such turn, the Server first estimates both requests. The prefixed request costs Core's instruction plus the final
response's output tokens at the full rate, plus the cached input tokens multiplied by
`POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_CACHED_INPUT_RATIO` (default `0.1`). Normal extraction costs its
instructions plus the Source evidence. The window must hold exactly one whole turn: one Source, or every part of one
turn in order. The Server uses the prefix only when that estimate is lower. This usually
holds for turns with substantial tool output, and stops holding as a long conversation's prefix grows. It then sends its extraction instruction, the output schema,
and the Source's provenance to `POST /gateway/v1/extract`. The gateway appends them as one user message after the
kept request and response, sets `tool_choice` to `none`, and sends it to the agent's own target and account. Fields
that would change the cached prefix, such as tools, `prompt_cache_key`, reasoning settings, and a response format, are
not changed. The request passes the Server's normal `before_send` and `before_release` checks. The prefix holds the
memory blocks the gateway injected; the extraction instruction states that content between `BEGIN_POWERCONTEXT_*` and
`END_POWERCONTEXT_*` markers is not evidence for the turn. An enforcing Server withholds a
request that replays the provider's encrypted reasoning, so with Codex and similar clients extraction then falls back
to normal extraction. It is not injected and is not captured. The Server validates the reply with the same schema
and rules as normal extraction.

Any other case falls back to normal extraction: a window that is not exactly one whole turn or has no handle, a prefix that is not
estimated to cost less or whose size is unknown, an expired or unknown
handle, a target mismatch, a withheld or blocked check, a provider or gateway error, or a reply that fails validation.
Each attempt is logged as `atomic_memory.prefixed_extraction`; see
[Logs, metrics, and tracing](observability.md#operational-fields-and-correlation). Model usage, including cached input
tokens, is reported like any other generation.

## Run with Docker

Build the image from the repository root:
Expand Down
7 changes: 7 additions & 0 deletions docs/en/docs/operate/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ When `auto` is set and the model provider prefix is outside `anthropic`, `openai
`openai-responses`, an `inference.generation.streaming_not_applied` WARNING with `unit` `inference` reports that
the model stayed on a non-streaming request. The event carries no prompt or response content and does not change
the request result.
With prefixed extraction configured (`POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_URL`), an INFO
`atomic_memory.prefixed_extraction` event with `operation` `atomic_memory.extract` and `unit` `inference` reports each
attempt for a window that holds one gateway turn with a prefix handle. `outcome` is `success` when the reply over the
cached prefix was used, or `fallback` when normal extraction ran instead; `fallback_reason` is then one of
`not_cheaper`, `prefix_size_unknown`, `prefix_unavailable`, `target_mismatch`, `disabled`, `invalid_instruction`, `check_rejected`, `gateway_unavailable`,
`provider_error`, `gateway_error`, or `invalid_output`. Windows without such a turn emit no event. The event carries no handle, prompt,
or response content.
For child cancellation, the supervisor emits `artifact_processing.failed` at `INFO` with outcome/category `cancelled`
and diagnostic code `worker_cancelled`. Application spans classify mapped diagnostic codes even when sensitive
exception values are excluded from logs and traces.
Expand Down
22 changes: 19 additions & 3 deletions docs/zh/development/capture-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ prefix extraction 扩展。不得加入正文、路径、message ID 列表、pro
| 字段 | 契约 |
| --- | --- |
| `schema` | 固定为 `powercontext.capture-coverage.v1`。 |
| `projection` | 下表定义的三个 profile 之一。 |
| `projection` | 下表定义的四个 profile 之一。 |
| `unit` | 与 profile 精确配对的投影单元。 |
| `status` | `complete`、`partial` 或 `unknown`。 |
| `reasons` | 受支持字符串,去重并按字典序升序排列。 |
Expand All @@ -61,7 +61,8 @@ reason 重复或乱序、status 与 reasons 矛盾,都解释为 unknown。解
| --- | --- | --- |
| `hermes-turn-text.v1` | `user_assistant_text_pair` | 当前规范化后的 user/assistant 文本对,包含既有角色标签和分隔符。 |
| `hermes-precompress-text.v1` | `new_user_assistant_window` | 宿主提供、经现有过滤及重叠去重后,本次待捕获的新 user/assistant 条目窗口。 |
| `gateway-turn-text.v1` | `gateway_turn_projection` | 当前受支持的 Chat/Responses 规范投影,从最后一条 user 到 terminal assistant。 |
| `gateway-turn-text.v1` | `gateway_turn_projection` | 早期的 Chat/Responses 规范投影,从最后一条 user 到 terminal assistant,字段与整条记录有字节上限。只用于解读已有 Source。 |
| `gateway-turn-text.v2` | `gateway_turn_projection` | Gateway 当前写入的完整回合投影,范围同 v1,不截断、不省略。 |

### Hermes completed turn

Expand All @@ -85,7 +86,22 @@ fingerprint 和 overlap 处理。投影单元是本次请求捕获的完整新
Scope 固定、重叠身份和 checkpoint 状态转换保持现有语义。capture 已持久化之后,后续 extraction
失败不否定 checkpoint。

### Gateway normalized turn
### Gateway complete turn(v2)

Gateway 新写入的 Source 使用 `gateway-turn-text.v2`。它保留这一回合中模型读到的全部内容:用户文本、其后的其他消息、
推理的可见摘要、工具调用的全部参数字段、工具输出与错误、内置工具返回的结果和无法识别的条目,以及最终助手文本。
字段不截断,步骤不省略。注入的 prepare、推理密文、ID、状态和其他传输元数据不在 profile 内;`data:` 二进制内容记为带
媒体类型和字节数的占位文本。排除这些内容本身不使投影变为 partial。

为使每段 Source 都能被 Core 的普通抽取单独处理(默认输入预算 24,000 个估算 token),一个回合按 Core 的字符估算
(转义后的文本,ASCII 每 4 个字符、其他每个字符各一个 token)超过 12,000 个 token 时,按顺序拆成几段 Source,每段都是完整的 JSON 文档,带 `part` 与
`parts`;单个步骤放不下时,其最长的文本字段拆成连续的同类步骤,后续部分带 `"continued": true`。拆分不丢失内容,
每段的声明都是 `complete`。各段元数据中的 `turn` 给出所属回合的 `source_id` 与段号。终止响应的判定与 v1 相同。

### Gateway normalized turn(v1)

以下描述 v1 投影,供解读 v1 声明的已有 Source;Gateway 不再写入 v1。


继续保留既有可见文本、工具调用和工具输出投影。注入的 prepare、opaque reasoning、传输 ID 及不支持
的媒体仍在 profile 之外;排除它们本身不代表该投影 partial。
Expand Down
3 changes: 3 additions & 0 deletions docs/zh/docs/operate/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ MCP 使用 stateless Streamable HTTP,每次工具执行都采用当前请求
| `POWERCONTEXT_SERVER_INFERENCE_GENERATION_STREAMING` | `disabled` | `auto` 时,仅模型名前缀为 `anthropic`、`openai`、`openai-chat`、`openai-responses` 的 generation 模型改用流式请求并在本地聚合为完整响应。其他前缀仍走非流式。`disabled` 时全部保持非流式请求 |
| `POWERCONTEXT_SERVER_INFERENCE_GENERATION_STREAM_IDLE_TIMEOUT_SECONDS` | `60` | 流式聚合时的最大空闲秒数:覆盖等待流建立并收到首个模型事件,以及相邻模型事件(正文、思考、工具调用)之间的间隔。供应商心跳不重置这个计时,超过即按超时处理 |
| `POWERCONTEXT_SERVER_INFERENCE_GENERATION_MODEL_CONTEXT_WINDOW_TOKENS` | `125000` | Topic 处理预算使用的 generation model 总上下文窗口 |
| `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_URL` | 未设置 | 同一主机上网关的 base URL;为单个网关回合复用其缓存前缀抽取 Atomic Memory,失败时回退到普通抽取。需要 `GENERATION_MODEL`。参见[复用缓存前缀抽取记忆](deploy-server.md#复用缓存前缀抽取记忆) |
| `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_CACHED_INPUT_RATIO` | `0.1` | Agent 目标上命中缓存的输入 token 相对未命中缓存的价格比例,大于 0 且不超过 1;只有估算复用前缀比普通抽取更便宜时才复用 |
| `POWERCONTEXT_SERVER_INFERENCE_PREFIX_EXTRACTION_GATEWAY_HEADERS` | `{}` | 发往网关的静态 header JSON 对象,例如网关的 `Authorization` 密钥;值为 secret |
| `POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_MODEL` | 未设置 | Pydantic AI embedding model;必须同时设置 profile ID 和 dimension |
| `POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_BASE_URL` | provider 默认值 | 自定义 OpenAI-compatible embeddings base URL |
| `POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_HEADERS` | `{}` | embedding client 静态 header JSON object;value 按 secret 处理 |
Expand Down
Loading
Loading