@caeliq/llms 1.0.60 → 1.0.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -3
- package/dist/cjs/server.cjs +245 -214
- package/dist/cjs/server.cjs.map +4 -4
- package/dist/esm/server.mjs +246 -215
- package/dist/esm/server.mjs.map +4 -4
- package/dist/server.d.ts +2 -0
- package/dist/tests/assistant-turn-order.d.ts +1 -0
- package/dist/tests/cache-prefix-debug.d.ts +1 -0
- package/dist/tests/cross-protocol.cache-prefix.d.ts +1 -0
- package/dist/tests/cross-protocol.config-matrix.d.ts +1 -0
- package/dist/tests/cross-protocol.responses-grok.d.ts +1 -0
- package/dist/tests/mistral.thinking-history.d.ts +1 -0
- package/dist/tests/responses.reasoning-duplication.d.ts +1 -0
- package/dist/tests/sse-debug-tap.d.ts +1 -0
- package/dist/tests/sse.client-keepalive.d.ts +1 -0
- package/dist/tests/xai-auth.reliability.d.ts +1 -0
- package/dist/transformer/index.d.ts +2 -0
- package/dist/transformer/openai.responses.transformer.d.ts +1 -0
- package/dist/transformer/xai-auth.transformer.d.ts +24 -0
- package/dist/types/llm.d.ts +7 -0
- package/dist/utils/cache-prefix-debug.d.ts +120 -0
- package/dist/utils/health-reporter.d.ts +13 -0
- package/dist/utils/openai.responses.util.d.ts +149 -0
- package/dist/utils/sse/client-keepalive.d.ts +19 -0
- package/dist/utils/sse/index.d.ts +1 -0
- package/dist/utils/sse-debug-tap.d.ts +62 -0
- package/dist/utils/xai-auth.d.ts +37 -0
- package/package.json +12 -12
package/dist/server.d.ts
CHANGED
|
@@ -60,3 +60,5 @@ export { sanitizeHeadersForLog, diffHeadersForLog, sanitizeBodyForLog, DEFAULT_L
|
|
|
60
60
|
export { exchangeAuthorizationCode, fetchUserEmail, resolveProjectId, saveTokens, loadTokens, getValidAccessToken, ANTIGRAVITY_CLIENT_ID, ANTIGRAVITY_CLIENT_SECRET, ANTIGRAVITY_REDIRECT_URI, ANTIGRAVITY_SCOPES, type AntigravityTokens, } from "./utils/antigravity-auth";
|
|
61
61
|
export { matchClientProtocol, isRoutedLlmPost, listClientRouteRegistrations, type ClientProtocol, type ClientProtocolContext, type ProtocolRouteMatch, } from "./routing/protocol-endpoints";
|
|
62
62
|
export { protocolErrorBody } from "./routing/protocol-errors";
|
|
63
|
+
export { setHealthReporter } from "./utils/health-reporter";
|
|
64
|
+
export type { HealthReporter } from "./utils/health-reporter";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -29,6 +29,7 @@ import { OpencodeHeadersTransformer } from "./opencode-headers.transformer";
|
|
|
29
29
|
import { ClaudeAuthTransformer } from "./claude-auth.transformer";
|
|
30
30
|
import { CursorSdkTransformer } from "./cursor-sdk.transformer";
|
|
31
31
|
import { AntigravityAuthTransformer } from "./antigravity-auth.transformer";
|
|
32
|
+
import { XaiAuthTransformer } from "./xai-auth.transformer";
|
|
32
33
|
declare const _default: {
|
|
33
34
|
AnthropicTransformer: typeof AnthropicTransformer;
|
|
34
35
|
GeminiTransformer: typeof GeminiTransformer;
|
|
@@ -61,5 +62,6 @@ declare const _default: {
|
|
|
61
62
|
ClaudeAuthTransformer: typeof ClaudeAuthTransformer;
|
|
62
63
|
CursorSdkTransformer: typeof CursorSdkTransformer;
|
|
63
64
|
AntigravityAuthTransformer: typeof AntigravityAuthTransformer;
|
|
65
|
+
XaiAuthTransformer: typeof XaiAuthTransformer;
|
|
64
66
|
};
|
|
65
67
|
export default _default;
|
|
@@ -23,6 +23,7 @@ export declare class OpenAIResponsesTransformer implements Transformer {
|
|
|
23
23
|
* allocated per Responses item by `toolIndexFor`.
|
|
24
24
|
*/
|
|
25
25
|
private convertStreamEvent;
|
|
26
|
+
private convertStreamEventCore;
|
|
26
27
|
private normalizeRequestContent;
|
|
27
28
|
private convertResponseToChat;
|
|
28
29
|
private buildImageContent;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Transformer } from "../types/transformer";
|
|
2
|
+
export declare class XaiAuthTransformer implements Transformer {
|
|
3
|
+
name: string;
|
|
4
|
+
logger?: any;
|
|
5
|
+
private resolveAuth;
|
|
6
|
+
/**
|
|
7
|
+
* openai-responses only converts the body — it doesn't own the outbound
|
|
8
|
+
* URL (its endPoint is a client-facing inbound route registration, not an
|
|
9
|
+
* outbound path). The generic pipeline falls back to a bare
|
|
10
|
+
* `provider.baseUrl` when no transformer sets config.url (routes.ts:739),
|
|
11
|
+
* so this transformer must build the actual `/responses` URL itself,
|
|
12
|
+
* mirroring CodexTransformer's `${baseUrl}/responses`.
|
|
13
|
+
*/
|
|
14
|
+
private buildConfig;
|
|
15
|
+
transformRequestIn(request: any, provider: any): Promise<Record<string, any>>;
|
|
16
|
+
auth(_request: any, provider: any): Promise<any>;
|
|
17
|
+
/**
|
|
18
|
+
* 401 recovery. PAT mode has nothing to recover — a bad literal key or
|
|
19
|
+
* env value can't be fixed by CCR. OAuth mode reloads the token file
|
|
20
|
+
* (another process may have already refreshed it), otherwise refreshes
|
|
21
|
+
* and persists, mirroring ClaudeAuthTransformer.recoverUnauthorizedAuth.
|
|
22
|
+
*/
|
|
23
|
+
private recoverUnauthorizedAuth;
|
|
24
|
+
}
|
package/dist/types/llm.d.ts
CHANGED
|
@@ -63,7 +63,12 @@ export interface UnifiedMessage {
|
|
|
63
63
|
};
|
|
64
64
|
thinking?: {
|
|
65
65
|
content: string;
|
|
66
|
+
/** Anthropic thinking signature; never treat as Responses ciphertext. */
|
|
66
67
|
signature?: string;
|
|
68
|
+
/** Provider-minted Responses/Codex reasoning ciphertext for replay. */
|
|
69
|
+
encrypted_content?: string;
|
|
70
|
+
/** Responses reasoning item id (`rs_…`); not interchangeable with ciphertext. */
|
|
71
|
+
id?: string;
|
|
67
72
|
};
|
|
68
73
|
}
|
|
69
74
|
export interface UnifiedTool {
|
|
@@ -161,6 +166,8 @@ export interface StreamChunk {
|
|
|
161
166
|
thinking?: {
|
|
162
167
|
content?: string;
|
|
163
168
|
signature?: string;
|
|
169
|
+
encrypted_content?: string;
|
|
170
|
+
id?: string;
|
|
164
171
|
};
|
|
165
172
|
tool_calls?: Array<{
|
|
166
173
|
id?: string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline position a snapshot was taken at. `client` is the Unified/exact-wire
|
|
3
|
+
* body handed to the provider chain; `wire` is what actually left for upstream.
|
|
4
|
+
* Comparing the two attributes a broken prefix to the client or to our own
|
|
5
|
+
* transformer chain without a bisect.
|
|
6
|
+
*/
|
|
7
|
+
export type CachePrefixStage = "client" | "wire";
|
|
8
|
+
export type CacheAffinityHeaders = {
|
|
9
|
+
sessionId?: string;
|
|
10
|
+
threadId?: string;
|
|
11
|
+
clientRequestId?: string;
|
|
12
|
+
};
|
|
13
|
+
export type CachePrefixSegment = {
|
|
14
|
+
path: string;
|
|
15
|
+
role?: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
hash: string;
|
|
18
|
+
/** Rough token weight (JSON chars / 4) — enough to rank misses by cost. */
|
|
19
|
+
approxTokens: number;
|
|
20
|
+
breakpoints: number;
|
|
21
|
+
reasoningId?: string;
|
|
22
|
+
};
|
|
23
|
+
export type CachePrefixSnapshot = {
|
|
24
|
+
createdAt: number;
|
|
25
|
+
model?: string;
|
|
26
|
+
prompt_cache_key?: string;
|
|
27
|
+
session_id?: string;
|
|
28
|
+
affinity?: CacheAffinityHeaders;
|
|
29
|
+
lastAssistantBlockOrder?: string[];
|
|
30
|
+
breakpointPaths: string[];
|
|
31
|
+
systemHash?: string;
|
|
32
|
+
toolsHash?: string;
|
|
33
|
+
approxTokens: number;
|
|
34
|
+
unstableIds: string[];
|
|
35
|
+
segments: CachePrefixSegment[];
|
|
36
|
+
};
|
|
37
|
+
export type CachePrefixChange = "none" | "appended" | "modified" | "removed";
|
|
38
|
+
export type CachePrefixIdSource = "session" | "cache_key" | "fingerprint";
|
|
39
|
+
export type CachePrefixDiff = {
|
|
40
|
+
conversationId: string;
|
|
41
|
+
conversationIdSource: CachePrefixIdSource;
|
|
42
|
+
stage: CachePrefixStage;
|
|
43
|
+
firstTurn: boolean;
|
|
44
|
+
prefixIntact: boolean;
|
|
45
|
+
change: CachePrefixChange;
|
|
46
|
+
unchangedPrefixCount: number;
|
|
47
|
+
previousSegmentCount: number;
|
|
48
|
+
currentSegmentCount: number;
|
|
49
|
+
/** Approximate tokens of prefix that stayed byte-identical. */
|
|
50
|
+
unchangedPrefixApproxTokens: number;
|
|
51
|
+
/** Approximate tokens the provider must re-read because the prefix moved. */
|
|
52
|
+
approxPrefixTokensLost: number;
|
|
53
|
+
/** Gap since the previous turn — separates a real break from TTL expiry. */
|
|
54
|
+
msSinceLastTurn?: number;
|
|
55
|
+
prompt_cache_keyChanged: boolean;
|
|
56
|
+
affinityChanged: boolean;
|
|
57
|
+
lastAssistantBlockOrderChanged: boolean;
|
|
58
|
+
modelChanged: boolean;
|
|
59
|
+
systemHashChanged: boolean;
|
|
60
|
+
toolsHashChanged: boolean;
|
|
61
|
+
breakpointsMoved: boolean;
|
|
62
|
+
/** Ids carrying an embedded timestamp — they rewrite the prefix every turn. */
|
|
63
|
+
unstableIds?: string[];
|
|
64
|
+
firstDivergencePath?: string;
|
|
65
|
+
firstDivergence?: {
|
|
66
|
+
previous?: DivergenceSide;
|
|
67
|
+
current?: DivergenceSide;
|
|
68
|
+
};
|
|
69
|
+
appendedPaths?: string[];
|
|
70
|
+
removedPaths?: string[];
|
|
71
|
+
prompt_cache_key?: {
|
|
72
|
+
previous?: string;
|
|
73
|
+
current?: string;
|
|
74
|
+
};
|
|
75
|
+
affinity?: {
|
|
76
|
+
previous?: CacheAffinityHeaders;
|
|
77
|
+
current?: CacheAffinityHeaders;
|
|
78
|
+
};
|
|
79
|
+
lastAssistantBlockOrder?: {
|
|
80
|
+
previous?: string[];
|
|
81
|
+
current?: string[];
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export type CachePrefixDiffOptions = {
|
|
85
|
+
stage?: CachePrefixStage;
|
|
86
|
+
provider?: string;
|
|
87
|
+
model?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Persist this snapshot as the baseline for the next turn. Pass false when
|
|
90
|
+
* upstream rejected the request — a body that was never cached must not
|
|
91
|
+
* become the baseline the following turn is judged against.
|
|
92
|
+
*/
|
|
93
|
+
commit?: boolean;
|
|
94
|
+
};
|
|
95
|
+
export declare function __resetCachePrefixSnapshotsForTests(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Compact, content-free snapshot of the outbound fields prompt caching uses.
|
|
98
|
+
*/
|
|
99
|
+
export declare function snapshotOutboundCachePrefix(body: Record<string, any> | null | undefined, affinity?: CacheAffinityHeaders): CachePrefixSnapshot | null;
|
|
100
|
+
type DivergenceSide = Pick<CachePrefixSegment, "path" | "role" | "type" | "approxTokens" | "breakpoints" | "reasoningId">;
|
|
101
|
+
export declare function diffCachePrefixSnapshots(conversationId: string, previous: CachePrefixSnapshot | undefined, current: CachePrefixSnapshot, meta?: {
|
|
102
|
+
stage?: CachePrefixStage;
|
|
103
|
+
conversationIdSource?: CachePrefixIdSource;
|
|
104
|
+
}): CachePrefixDiff;
|
|
105
|
+
/**
|
|
106
|
+
* Compare this outbound body to the last one for the conversation, then
|
|
107
|
+
* remember the current snapshot. Returns null when there is nothing cacheable.
|
|
108
|
+
*
|
|
109
|
+
* Snapshots are keyed per stage and per provider/model: a routed fallback to a
|
|
110
|
+
* different destination is a legitimate cache miss, not prefix corruption, and
|
|
111
|
+
* must not be reported as one.
|
|
112
|
+
*/
|
|
113
|
+
export declare function rememberAndDiffOutboundCachePrefix(conversationId: string | undefined, body: Record<string, any> | null | undefined, affinity?: CacheAffinityHeaders, options?: CachePrefixDiffOptions): CachePrefixDiff | null;
|
|
114
|
+
/**
|
|
115
|
+
* Attribute a broken wire prefix to the stage that broke it. The client leg is
|
|
116
|
+
* checked first: if Claude Code itself rewrote history there is nothing our
|
|
117
|
+
* transformer chain could have preserved.
|
|
118
|
+
*/
|
|
119
|
+
export declare function attributeDivergenceStage(clientDiff: CachePrefixDiff | null | undefined, wireDiff: CachePrefixDiff | null | undefined): CachePrefixStage | "none" | undefined;
|
|
120
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional process vitals for the `/health` probe.
|
|
3
|
+
*
|
|
4
|
+
* The API routes are registered inside encapsulated Fastify plugins, which
|
|
5
|
+
* snapshot the instance they inherit from at registration time — a decorator
|
|
6
|
+
* added to the root afterwards is invisible to them. Whoever owns the process
|
|
7
|
+
* (the server package, which builds its health heartbeat after the routes are
|
|
8
|
+
* up) therefore publishes the reporter here instead.
|
|
9
|
+
*/
|
|
10
|
+
export type HealthReporter = () => unknown;
|
|
11
|
+
export declare function setHealthReporter(fn: HealthReporter | undefined): void;
|
|
12
|
+
/** Vitals for the current instant, or `undefined` when none are published. */
|
|
13
|
+
export declare function readHealthVitals(): unknown;
|
|
@@ -12,22 +12,166 @@ export declare function mapCallId(map: ResponsesCallIdMap, id: unknown, directio
|
|
|
12
12
|
* Supports the Responses MVP subset; rejects CCR-unsupported stateful fields.
|
|
13
13
|
*/
|
|
14
14
|
export declare function responsesRequestToUnified(body: any, callIdMap?: ResponsesCallIdMap, customToolNames?: Set<string>): UnifiedChatRequest;
|
|
15
|
+
export declare function isResponsesReasoningItemId(value: unknown): value is string;
|
|
16
|
+
/**
|
|
17
|
+
* Ciphertext Codex/OpenAI will verify. Item ids and Anthropic/Gemini
|
|
18
|
+
* signatures are not encrypted_content — replaying them 400s with
|
|
19
|
+
* `invalid_encrypted_content`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function responsesEncryptedContentFrom(value: unknown): string | undefined;
|
|
22
|
+
export interface UnifiedAssistantThinking {
|
|
23
|
+
content: string;
|
|
24
|
+
signature?: string;
|
|
25
|
+
encrypted_content?: string;
|
|
26
|
+
id?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Anthropic/Gemini thinking.signature — never a Responses item id. */
|
|
29
|
+
export declare function anthropicThinkingSignatureFrom(thinking: {
|
|
30
|
+
signature?: string;
|
|
31
|
+
} | undefined): string | undefined;
|
|
32
|
+
/** Responses `reasoning` item → Unified assistant.thinking. */
|
|
33
|
+
export declare function thinkingFromResponsesReasoningItem(item: any): UnifiedAssistantThinking | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Record streamed reasoning summary text for one item. Late handlers
|
|
36
|
+
* (`output_item.done`, `response.completed`) consult this map so they can
|
|
37
|
+
* emit ciphertext / id without re-sending content — Unified
|
|
38
|
+
* `delta.thinking.content` is additive in every downstream consumer.
|
|
39
|
+
*/
|
|
40
|
+
export declare function recordReasoningSummaryDelta(deliveredContentByItemId: Map<string, string>, itemId: unknown, delta: unknown): void;
|
|
41
|
+
/**
|
|
42
|
+
* Late Responses reasoning handlers exist to rescue `encrypted_content` /
|
|
43
|
+
* item id after summary deltas have already streamed the text. If this
|
|
44
|
+
* item's content was already delivered on the current stream, return
|
|
45
|
+
* replay metadata only. A terminal-only reasoning item (no summary
|
|
46
|
+
* deltas) still delivers content exactly once.
|
|
47
|
+
*/
|
|
48
|
+
export declare function thinkingForLateReasoningItem(item: any, deliveredContentByItemId: Map<string, string>): UnifiedAssistantThinking | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Inverse of the inbound `text.format` → Chat Completions `response_format`
|
|
51
|
+
* mapping in `responsesRequestToUnified`. Shared by Codex and generic
|
|
52
|
+
* Responses outbound so the two reconstruct sites cannot drift.
|
|
53
|
+
*/
|
|
54
|
+
export declare function responsesTextFormatFromResponseFormat(responseFormat: any): {
|
|
55
|
+
type: string;
|
|
56
|
+
name?: string;
|
|
57
|
+
schema?: any;
|
|
58
|
+
strict?: boolean;
|
|
59
|
+
} | undefined;
|
|
60
|
+
/** Unified assistant.thinking → Responses `reasoning` input/output item. */
|
|
61
|
+
export declare function thinkingFromUnifiedAssistant(message: any): UnifiedAssistantThinking | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Fixed assistant-turn order for every protocol:
|
|
64
|
+
* thinking → text → images → tool calls.
|
|
65
|
+
* Reordering here keeps cache prefixes stable across Anthropic, Chat,
|
|
66
|
+
* Responses, Gemini, and Mistral.
|
|
67
|
+
*/
|
|
68
|
+
export interface CanonicalAssistantTurn {
|
|
69
|
+
thinking?: UnifiedAssistantThinking;
|
|
70
|
+
texts: Array<{
|
|
71
|
+
text: string;
|
|
72
|
+
cache_control?: any;
|
|
73
|
+
}>;
|
|
74
|
+
images: any[];
|
|
75
|
+
toolCalls: any[];
|
|
76
|
+
}
|
|
77
|
+
export declare function canonicalAssistantTurn(message: any): CanonicalAssistantTurn;
|
|
78
|
+
export declare function assistantTurnHasText(turn: CanonicalAssistantTurn): boolean;
|
|
79
|
+
export declare function responsesReasoningItemFromThinking(thinking: {
|
|
80
|
+
content?: string;
|
|
81
|
+
signature?: string;
|
|
82
|
+
encrypted_content?: string;
|
|
83
|
+
id?: string;
|
|
84
|
+
} | undefined, id?: string): any | null;
|
|
15
85
|
/** Synthetic argument key used to carry a `custom` tool's freeform text
|
|
16
86
|
* through the Unified/Chat Completions function-call shape. */
|
|
17
87
|
export declare const CUSTOM_TOOL_INPUT_KEY = "input";
|
|
88
|
+
/**
|
|
89
|
+
* Codex's apply_patch/apply_update grammar accepts only the exact markers
|
|
90
|
+
* `*** Begin Patch` and `*** End Patch` (no trailing asterisk triplet) as the
|
|
91
|
+
* first/last patch lines — anything else is rejected with "The first line of
|
|
92
|
+
* the patch must be '*** Begin Patch'". Models not trained on that grammar
|
|
93
|
+
* (e.g. Grok reaches for Claude's trailing-asterisk variant) emit
|
|
94
|
+
* `*** Begin Patch ***` / `*** End Patch ***`. Normalize those to the Codex
|
|
95
|
+
* form on the client-facing emission, scoped by the marker token itself so a
|
|
96
|
+
* rewrite can only ever hit an apply_patch-style payload regardless of the
|
|
97
|
+
* declared tool name.
|
|
98
|
+
*/
|
|
99
|
+
export declare function normalizeCodexPatchMarkers(text: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Codex's `exec` tool runs raw JavaScript in a V8 isolate; a model that was
|
|
102
|
+
* never trained on that convention (Grok) sometimes fills the freeform input
|
|
103
|
+
* with a JSON shell-envelope instead — e.g. `{"cmd": "ls -la /tmp"}`. That is
|
|
104
|
+
* not valid JS (`{"cmd":…}` parses as a block with a string label →
|
|
105
|
+
* `SyntaxError: Unexpected token ':'`), so the shell call fails and the model
|
|
106
|
+
* wastes a turn retrying as `await tools.exec_command({cmd: …})`.
|
|
107
|
+
* Rewrite the envelope into exactly that retry shape on the client-facing
|
|
108
|
+
* emission. Scoped by the tool name and the whole-input-is-a-shell-object
|
|
109
|
+
* shape so a freeform tool that legitimately takes a JSON blob is never
|
|
110
|
+
* touched. Grok also alternates the key between `cmd` and `command`, and
|
|
111
|
+
* `exec_command` only accepts `cmd`, so normalize either to `cmd`.
|
|
112
|
+
*/
|
|
113
|
+
export declare function normalizeExecCommandEnvelope(name: string | undefined, text: string): string;
|
|
114
|
+
/**
|
|
115
|
+
* A model that reaches for the patch tool through a shell hereditary habit
|
|
116
|
+
* wraps the patch in a heredoc instead of JS:
|
|
117
|
+
* apply_patch << 'PATCH'\n*** Begin Patch … *** End Patch\nPATCH
|
|
118
|
+
* That is a shell command, not the raw JS `exec` runs, so it fails with a
|
|
119
|
+
* syntax error and the model wastes a turn retrying as
|
|
120
|
+
* `await tools.apply_patch(…)`. Normalize the heredoc invocation into the JS
|
|
121
|
+
* call form. Whole-value heredoc match only; anything else passes through.
|
|
122
|
+
*/
|
|
123
|
+
export declare function normalizeExecApplyPatchHeredoc(name: string | undefined, text: string): string;
|
|
124
|
+
/**
|
|
125
|
+
* `exec` may cross the wire as a plain function tool (arguments stay the JSON
|
|
126
|
+
* wrapper `{"input":"…"}`) rather than a custom tool (whose freeform input was
|
|
127
|
+
* unwrapped already). The envelope/heredoc normalizers above operate on the
|
|
128
|
+
* unwrapped inner text, so when only the wrapper is available, descend into it
|
|
129
|
+
* and normalize its inner `input` value with the same rules.
|
|
130
|
+
*/
|
|
131
|
+
export declare function normalizeExecFunctionArguments(name: string | undefined, rawArguments: string, options?: CodexIsolateConventionsOptions): string;
|
|
132
|
+
/** Undo the CUSTOM_TOOL_INPUT_KEY wrapping applied in normalizeResponsesTools.
|
|
133
|
+
* Falls back to the raw text so a malformed/empty call still round-trips
|
|
134
|
+
* instead of vanishing. Also strips a whole-value shell heredoc wrapper
|
|
135
|
+
* (see stripHeredocWrapper). That strip is intentional on both paths this
|
|
136
|
+
* helper serves: client emission (so the Responses client sees clean
|
|
137
|
+
* freeform text) and CodexTransformer history replay (so the backend is
|
|
138
|
+
* not re-fed a wrapper the client never kept). Replay is therefore
|
|
139
|
+
* normalized, not byte-faithful to the model's original heredoc. */
|
|
140
|
+
export declare function unwrapCustomToolInput(rawArguments: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* Codex V8 isolate calling conventions (`await tools.exec_command` /
|
|
143
|
+
* `await tools.apply_patch`). Default on: CCR's `/v1/responses` inbound
|
|
144
|
+
* is the Codex CLI path, and Grok-via-Chat recovery depends on it.
|
|
145
|
+
* Pass `false` for a generic Responses destination whose `exec` tool
|
|
146
|
+
* legitimately accepts `{"cmd": …}` JSON.
|
|
147
|
+
*/
|
|
148
|
+
export interface CodexIsolateConventionsOptions {
|
|
149
|
+
codexIsolateConventions?: boolean;
|
|
150
|
+
}
|
|
151
|
+
/** Client-facing custom-tool input: unwrap the Unified JSON wrapper, then
|
|
152
|
+
* apply the exec/patch normalizers in one place so stream finalize, the
|
|
153
|
+
* completed skeleton, and the non-stream JSON path cannot drift. */
|
|
154
|
+
export declare function normalizeClientCustomToolInput(name: string | undefined, rawArguments: string, options?: CodexIsolateConventionsOptions): string;
|
|
18
155
|
/** Unified Chat JSON → Responses API non-stream response. */
|
|
19
156
|
export declare function unifiedResponseToResponses(chat: any, options?: {
|
|
20
157
|
originalModel?: string;
|
|
21
158
|
callIdMap?: ResponsesCallIdMap;
|
|
22
159
|
customToolNames?: Set<string>;
|
|
160
|
+
codexIsolateConventions?: boolean;
|
|
23
161
|
}): any;
|
|
24
162
|
export interface ResponsesStreamState {
|
|
25
163
|
responseId: string;
|
|
26
164
|
model?: string;
|
|
27
165
|
textItemId: string;
|
|
28
166
|
textStarted: boolean;
|
|
167
|
+
textClosed: boolean;
|
|
29
168
|
textOutputIndex?: number;
|
|
30
169
|
textContent: string;
|
|
170
|
+
closedTextItems: Array<{
|
|
171
|
+
id: string;
|
|
172
|
+
outputIndex: number;
|
|
173
|
+
content: string;
|
|
174
|
+
}>;
|
|
31
175
|
toolCalls: Map<number, {
|
|
32
176
|
id: string;
|
|
33
177
|
name: string;
|
|
@@ -45,11 +189,16 @@ export interface ResponsesStreamState {
|
|
|
45
189
|
usage?: any;
|
|
46
190
|
callIdMap: ResponsesCallIdMap;
|
|
47
191
|
customToolNames: Set<string>;
|
|
192
|
+
thinkingContent: string;
|
|
193
|
+
thinkingEncryptedContent?: string;
|
|
194
|
+
thinkingId?: string;
|
|
195
|
+
codexIsolateConventions: boolean;
|
|
48
196
|
}
|
|
49
197
|
export declare function createResponsesStreamState(options?: {
|
|
50
198
|
model?: string;
|
|
51
199
|
callIdMap?: ResponsesCallIdMap;
|
|
52
200
|
customToolNames?: Set<string>;
|
|
201
|
+
codexIsolateConventions?: boolean;
|
|
53
202
|
}): ResponsesStreamState;
|
|
54
203
|
/**
|
|
55
204
|
* Convert one Unified Chat Completions chunk into zero or more Responses
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-facing SSE keepalive.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code shows "Waiting for API response · will retry in … · check your
|
|
5
|
+
* network" when no bytes arrive on the response stream for 20s (advisor: 90s),
|
|
6
|
+
* even before any retry has started — see Claude Code error docs. Anthropic's
|
|
7
|
+
* own `ping` events often arrive only every ~25–30s during long thinking /
|
|
8
|
+
* tool-argument streams, so a transparent proxy that only forwards upstream
|
|
9
|
+
* bytes trips that spinner on every slow Opus turn.
|
|
10
|
+
*
|
|
11
|
+
* Inject SSE comment frames (`: …\n\n`) after `idleMs` of silence. Comments are
|
|
12
|
+
* transport keepalives: EventSource / Anthropic SSE parsers ignore them, but
|
|
13
|
+
* they reset Claude Code's byte-idle timer. Default 10s is half the 20s warning.
|
|
14
|
+
*/
|
|
15
|
+
export type SSEClientKeepaliveOptions = {
|
|
16
|
+
/** Silence before emitting a comment frame. Default 10_000. */
|
|
17
|
+
idleMs?: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function withSSEClientKeepalive(body: ReadableStream<Uint8Array>, options?: SSEClientKeepaliveOptions): ReadableStream<Uint8Array>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { SSEParserTransform } from './SSEParser.transform';
|
|
2
2
|
export { SSESerializerTransform } from './SSESerializer.transform';
|
|
3
3
|
export { rewriteStream } from './rewriteStream';
|
|
4
|
+
export { withSSEClientKeepalive, type SSEClientKeepaliveOptions, } from './client-keepalive';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type CacheAffinityHeaders, type CachePrefixDiff, type CachePrefixStage } from "./cache-prefix-debug";
|
|
2
|
+
export type UpstreamSSEDebugOptions = {
|
|
3
|
+
logger?: any;
|
|
4
|
+
reqId?: string | number;
|
|
5
|
+
provider?: string;
|
|
6
|
+
/** Routed model — snapshots are keyed per destination. */
|
|
7
|
+
model?: string;
|
|
8
|
+
/** Conversation / Claude session id used to pair consecutive cache snapshots. */
|
|
9
|
+
conversationId?: string;
|
|
10
|
+
/** Pipeline position this body was captured at. Defaults to `wire`. */
|
|
11
|
+
stage?: CachePrefixStage;
|
|
12
|
+
/** Codex (and similar) routing headers that pin prompt-cache affinity. */
|
|
13
|
+
cacheAffinity?: CacheAffinityHeaders;
|
|
14
|
+
/** Upstream status. Non-2xx bodies are diffed but never become the baseline. */
|
|
15
|
+
responseStatus?: number;
|
|
16
|
+
/** Client-leg diff, used to attribute a broken wire prefix to a stage. */
|
|
17
|
+
clientStageDiff?: CachePrefixDiff | null;
|
|
18
|
+
/** Outbound diff for this request, joined with the observed cache usage. */
|
|
19
|
+
cacheDiff?: CachePrefixDiff | null;
|
|
20
|
+
/** Cap for a single logged payload string (raw `data` field). */
|
|
21
|
+
maxBytes?: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Byte-preserving upstream response debug tap.
|
|
25
|
+
*
|
|
26
|
+
* For SSE: mirrors bytes to a background consumer that emits Codex-parity
|
|
27
|
+
* `recieved data` / `Original Response` logs (including Anthropic usage /
|
|
28
|
+
* cache fields on message_start / message_delta).
|
|
29
|
+
*
|
|
30
|
+
* Important: do **not** use `ReadableStream.tee()` here. Tee couples
|
|
31
|
+
* backpressure across both branches — a slow debug logger (pino file I/O on
|
|
32
|
+
* every thinking delta) stalls the client branch. Claude Code then idles long
|
|
33
|
+
* enough to surface "Waiting for API response · check your network" and retry
|
|
34
|
+
* while CCR eventually still finishes the upstream as HTTP 200.
|
|
35
|
+
*
|
|
36
|
+
* Instead, a TransformStream forwards each chunk to the client immediately and
|
|
37
|
+
* copies into a debug tunnel whose writable side has an infinite high-water
|
|
38
|
+
* mark, so debug I/O can never delay the client. The debug consumer drains the
|
|
39
|
+
* buffered copy at its own pace (memory-bound to the stream size).
|
|
40
|
+
*
|
|
41
|
+
* Exact-wire passthrough never enters transformer stream loggers; this tap is
|
|
42
|
+
* the single shared place that covers every outbound provider.
|
|
43
|
+
*/
|
|
44
|
+
export declare function tapUpstreamSSEDebug(response: Response, opts: UpstreamSSEDebugOptions): Promise<Response>;
|
|
45
|
+
export type CacheStructureSummary = {
|
|
46
|
+
systemBreakpoints: number;
|
|
47
|
+
messageBreakpoints: number;
|
|
48
|
+
toolBreakpoints: number;
|
|
49
|
+
prompt_cache_key?: string;
|
|
50
|
+
lastAssistantBlockOrder?: string[];
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Summarize outbound Anthropic/OpenAI cache-oriented request structure for
|
|
54
|
+
* debug verification (breakpoints / prompt_cache_key / assistant block order).
|
|
55
|
+
*/
|
|
56
|
+
export declare function summarizeOutboundCacheStructure(body: Record<string, any> | null | undefined): CacheStructureSummary | null;
|
|
57
|
+
/**
|
|
58
|
+
* Snapshot the outbound body, diff it against the previous turn, and log both.
|
|
59
|
+
* Returns the diff so the caller can join it with the observed cache usage
|
|
60
|
+
* once upstream responds.
|
|
61
|
+
*/
|
|
62
|
+
export declare function logOutboundCacheStructure(body: Record<string, any> | null | undefined, opts: UpstreamSSEDebugOptions): CachePrefixDiff | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
declare const OAUTH_CONFIG: {
|
|
2
|
+
client_id: string;
|
|
3
|
+
device_authorization_endpoint: string;
|
|
4
|
+
token_endpoint: string;
|
|
5
|
+
scope: string;
|
|
6
|
+
};
|
|
7
|
+
export interface XaiTokens {
|
|
8
|
+
access_token: string;
|
|
9
|
+
refresh_token?: string;
|
|
10
|
+
id_token?: string;
|
|
11
|
+
token_type: string;
|
|
12
|
+
scope?: string;
|
|
13
|
+
expires_at?: number;
|
|
14
|
+
last_refresh?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface DeviceCodeResponse {
|
|
17
|
+
device_code: string;
|
|
18
|
+
user_code: string;
|
|
19
|
+
verification_uri: string;
|
|
20
|
+
verification_uri_complete?: string;
|
|
21
|
+
expires_in?: number;
|
|
22
|
+
interval?: number;
|
|
23
|
+
}
|
|
24
|
+
declare function getAuthFilePath(): string;
|
|
25
|
+
export declare function loadTokens(): XaiTokens | null;
|
|
26
|
+
export declare function saveTokens(tokens: XaiTokens): void;
|
|
27
|
+
export declare function isTokenExpiring(tokens: XaiTokens, skewSeconds?: number): boolean;
|
|
28
|
+
export declare function requestDeviceCode(): Promise<DeviceCodeResponse>;
|
|
29
|
+
export declare function pollDeviceCodeToken(device: DeviceCodeResponse, options?: {
|
|
30
|
+
sleep?: (ms: number) => Promise<void>;
|
|
31
|
+
now?: () => number;
|
|
32
|
+
}): Promise<XaiTokens>;
|
|
33
|
+
export declare function refreshTokens(_refreshToken?: string): Promise<XaiTokens>;
|
|
34
|
+
export declare function getValidAccessToken(options?: {
|
|
35
|
+
force?: boolean;
|
|
36
|
+
}): Promise<XaiTokens>;
|
|
37
|
+
export { getAuthFilePath, OAUTH_CONFIG };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caeliq/llms",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.61",
|
|
4
4
|
"description": "A universal LLM API transformation server",
|
|
5
5
|
"main": "dist/cjs/server.cjs",
|
|
6
6
|
"module": "dist/esm/server.mjs",
|
|
@@ -30,29 +30,29 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@anthropic-ai/sdk": "^0.115.0",
|
|
33
|
-
"@caeliq/ccr-shared": "^2.1.
|
|
34
|
-
"@cursor/sdk": "^1.0.
|
|
33
|
+
"@caeliq/ccr-shared": "^2.1.3",
|
|
34
|
+
"@cursor/sdk": "^1.0.28",
|
|
35
35
|
"@fastify/cors": "^11.3.0",
|
|
36
|
-
"@fastify/rate-limit": "^
|
|
37
|
-
"@google/genai": "^2.
|
|
36
|
+
"@fastify/rate-limit": "^11.2.0",
|
|
37
|
+
"@google/genai": "^2.17.1",
|
|
38
38
|
"@huggingface/tokenizers": "^0.1.3",
|
|
39
39
|
"dotenv": "^17.4.2",
|
|
40
|
-
"fastify": "^5.
|
|
40
|
+
"fastify": "^5.12.0",
|
|
41
41
|
"fastify-plugin": "^6.0.0",
|
|
42
|
-
"google-auth-library": "^11.0.
|
|
42
|
+
"google-auth-library": "^11.0.2",
|
|
43
43
|
"json5": "^2.2.3",
|
|
44
44
|
"jsonrepair": "^3.15.0",
|
|
45
45
|
"latex-to-unicode": "^0.1.0",
|
|
46
46
|
"lru-cache": "^11.5.2",
|
|
47
|
-
"openai": "^7.
|
|
47
|
+
"openai": "^7.4.0",
|
|
48
48
|
"tiktoken": "^1.0.22",
|
|
49
|
-
"undici": "^8.
|
|
49
|
+
"undici": "^8.10.0",
|
|
50
50
|
"uuid": "^14.0.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@types/node": "^26.
|
|
54
|
-
"esbuild": "^0.28.
|
|
55
|
-
"tsx": "^4.23.
|
|
53
|
+
"@types/node": "^26.2.0",
|
|
54
|
+
"esbuild": "^0.28.2",
|
|
55
|
+
"tsx": "^4.23.12",
|
|
56
56
|
"typescript": "^6.0.3"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|