@plumeai/plugin-sdk 0.0.0-stage → 0.1.2
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/LICENSE +21 -0
- package/bin/plume-plugin.mjs +70 -0
- package/dist/client/styles/src/index.d.ts +123 -0
- package/dist/client/ui/src/avatar/AgentAvatar.d.ts +42 -0
- package/dist/client/ui/src/avatar/AvatarPile.d.ts +36 -0
- package/dist/client/ui/src/avatar/AvatarStack.d.ts +26 -0
- package/dist/client/ui/src/avatar/art.d.ts +32 -0
- package/dist/client/ui/src/fileIcon.d.ts +33 -0
- package/dist/client/ui/src/hooks/useCopied.d.ts +16 -0
- package/dist/client/ui/src/hooks/useLiveRefresh.d.ts +22 -0
- package/dist/client/ui/src/hooks/useNarrow.d.ts +16 -0
- package/dist/client/ui/src/i18n/context.d.ts +17 -0
- package/dist/client/ui/src/i18n/index.d.ts +2 -0
- package/dist/client/ui/src/i18n/list.d.ts +20 -0
- package/dist/client/ui/src/i18n/locales.d.ts +10 -0
- package/dist/client/ui/src/i18n/messages/en.d.ts +1418 -0
- package/dist/client/ui/src/i18n/messages/index.d.ts +6 -0
- package/dist/client/ui/src/i18n/messages/zh-CN.d.ts +1263 -0
- package/dist/client/ui/src/i18n/plural.d.ts +16 -0
- package/dist/client/ui/src/i18n/translate.d.ts +43 -0
- package/dist/client/ui/src/icons/index.d.ts +5 -0
- package/dist/client/ui/src/image/AnnotateCanvas.d.ts +29 -0
- package/dist/client/ui/src/image/AnnotateToolbar.d.ts +73 -0
- package/dist/client/ui/src/image/Annotator.d.ts +154 -0
- package/dist/client/ui/src/image/annotate.d.ts +227 -0
- package/dist/client/ui/src/image/paint.d.ts +35 -0
- package/dist/client/ui/src/image/screenshot-geometry.d.ts +112 -0
- package/dist/client/ui/src/image/useBarDrag.d.ts +31 -0
- package/dist/client/ui/src/image/useMovableBar.d.ts +26 -0
- package/dist/client/ui/src/image/useToolbarPlacement.d.ts +42 -0
- package/dist/client/ui/src/index.d.ts +140 -0
- package/dist/client/ui/src/inputs/ColorField.d.ts +9 -0
- package/dist/client/ui/src/inputs/ComposerToggle.d.ts +23 -0
- package/dist/client/ui/src/inputs/DateRangeField.d.ts +25 -0
- package/dist/client/ui/src/inputs/NativeField.d.ts +5 -0
- package/dist/client/ui/src/inputs/SearchField.d.ts +21 -0
- package/dist/client/ui/src/inputs/Segmented.d.ts +23 -0
- package/dist/client/ui/src/inputs/Select.d.ts +32 -0
- package/dist/client/ui/src/inputs/ShortcutRecorder.d.ts +8 -0
- package/dist/client/ui/src/inputs/Slider.d.ts +13 -0
- package/dist/client/ui/src/inputs/TextArea.d.ts +30 -0
- package/dist/client/ui/src/inputs/Toggle.d.ts +10 -0
- package/dist/client/ui/src/inputs/WorktreeToggle.d.ts +19 -0
- package/dist/client/ui/src/inputs/day-range.d.ts +57 -0
- package/dist/client/ui/src/inputs/pickers.d.ts +40 -0
- package/dist/client/ui/src/inputs/useFieldFade.d.ts +1 -0
- package/dist/client/ui/src/keyboard.d.ts +21 -0
- package/dist/client/ui/src/layout/ApprovalFrame.d.ts +33 -0
- package/dist/client/ui/src/layout/Collapse.d.ts +9 -0
- package/dist/client/ui/src/layout/CopyMark.d.ts +19 -0
- package/dist/client/ui/src/layout/Disclosure.d.ts +66 -0
- package/dist/client/ui/src/layout/ErrorBoundary.d.ts +26 -0
- package/dist/client/ui/src/layout/PageTopRow.d.ts +19 -0
- package/dist/client/ui/src/layout/PanelEmpty.d.ts +17 -0
- package/dist/client/ui/src/layout/ResizeHandle.d.ts +24 -0
- package/dist/client/ui/src/layout/RetainedViews.d.ts +32 -0
- package/dist/client/ui/src/layout/Settings.d.ts +41 -0
- package/dist/client/ui/src/layout/Splitter.d.ts +25 -0
- package/dist/client/ui/src/lib/agent-avatar.d.ts +58 -0
- package/dist/client/ui/src/lib/code/font-stack.d.ts +12 -0
- package/dist/client/ui/src/lib/code/themes.d.ts +35 -0
- package/dist/client/ui/src/lib/date-format.d.ts +8 -0
- package/dist/client/ui/src/lib/dispatches.d.ts +8 -0
- package/dist/client/ui/src/lib/hour-style.d.ts +13 -0
- package/dist/client/ui/src/lib/number-draft.d.ts +24 -0
- package/dist/client/ui/src/lib/relative-time.d.ts +18 -0
- package/dist/client/ui/src/lib/tiling.d.ts +89 -0
- package/dist/client/ui/src/lib/typing.d.ts +19 -0
- package/dist/client/ui/src/motion/RollWords.d.ts +26 -0
- package/dist/client/ui/src/motion/RollingText.d.ts +43 -0
- package/dist/client/ui/src/motion/TypedText.d.ts +9 -0
- package/dist/client/ui/src/motion/freeze.d.ts +22 -0
- package/dist/client/ui/src/motion/held.d.ts +17 -0
- package/dist/client/ui/src/motion/loaders.d.ts +65 -0
- package/dist/client/ui/src/motion/reduced.d.ts +13 -0
- package/dist/client/ui/src/motion/ticked.d.ts +24 -0
- package/dist/client/ui/src/motion/ticker.d.ts +31 -0
- package/dist/client/ui/src/motion/tokens.d.ts +29 -0
- package/dist/client/ui/src/motion/useNow.d.ts +6 -0
- package/dist/client/ui/src/overlay/Confirm.d.ts +96 -0
- package/dist/client/ui/src/overlay/ContextMenu.d.ts +40 -0
- package/dist/client/ui/src/overlay/Dialog.d.ts +60 -0
- package/dist/client/ui/src/overlay/Menu.d.ts +90 -0
- package/dist/client/ui/src/overlay/Overlay.d.ts +17 -0
- package/dist/client/ui/src/overlay/Popover.d.ts +127 -0
- package/dist/client/ui/src/overlay/Tooltip.d.ts +14 -0
- package/dist/client/ui/src/overlay/afterimage.d.ts +20 -0
- package/dist/client/ui/src/overlay/chrome-top.d.ts +8 -0
- package/dist/client/ui/src/overlay/hover-layers.d.ts +41 -0
- package/dist/client/ui/src/overlay/portal.d.ts +21 -0
- package/dist/client/ui/src/overlay/scrims.d.ts +20 -0
- package/dist/client/ui/src/overlay/tooltip-attribute.d.ts +62 -0
- package/dist/client/ui/src/primitives/Button.d.ts +64 -0
- package/dist/client/ui/src/primitives/Caret.d.ts +25 -0
- package/dist/client/ui/src/primitives/ChoiceMark.d.ts +12 -0
- package/dist/client/ui/src/primitives/ClosableTab.d.ts +23 -0
- package/dist/client/ui/src/primitives/CountUp.d.ts +21 -0
- package/dist/client/ui/src/primitives/GitHubMark.d.ts +12 -0
- package/dist/client/ui/src/primitives/IconButton.d.ts +55 -0
- package/dist/client/ui/src/primitives/MarkNumber.d.ts +12 -0
- package/dist/client/ui/src/primitives/RowDeleteButton.d.ts +6 -0
- package/dist/client/ui/src/primitives/Skeleton.d.ts +36 -0
- package/dist/client/ui/src/primitives/TabStrip.d.ts +29 -0
- package/dist/client/ui/src/primitives/Text.d.ts +341 -0
- package/dist/client/ui/src/primitives/TimeAgo.d.ts +13 -0
- package/dist/client/ui/src/primitives/useCountUp.d.ts +23 -0
- package/dist/client/ui/src/row/FlowRow.d.ts +31 -0
- package/dist/client/ui/src/row/HoverRow.d.ts +28 -0
- package/dist/client/ui/src/scroll/BackToLatest.d.ts +19 -0
- package/dist/client/ui/src/scroll/OverlayScrollbar.d.ts +9 -0
- package/dist/client/ui/src/scroll/ScrollText.d.ts +24 -0
- package/dist/client/ui/src/scroll/Scroller.d.ts +66 -0
- package/dist/client/ui/src/scroll/Sideways.d.ts +30 -0
- package/dist/client/ui/src/scroll/fade.d.ts +2 -0
- package/dist/client/ui/src/scroll/follow.d.ts +157 -0
- package/dist/client/ui/src/scroll/measure-batch.d.ts +13 -0
- package/dist/client/ui/src/scroll/memory.d.ts +22 -0
- package/dist/client/ui/src/scroll/signature.d.ts +24 -0
- package/dist/client/ui/src/scroll/useFollowBottom.d.ts +80 -0
- package/dist/client/ui/src/scroll/useSideways.d.ts +29 -0
- package/dist/contract/src/app.d.ts +16 -0
- package/dist/contract/src/changes.d.ts +50 -0
- package/dist/contract/src/diagnostics.d.ts +86 -0
- package/dist/contract/src/index.d.ts +18 -0
- package/dist/contract/src/plugin.d.ts +1391 -0
- package/dist/contract/src/resources.d.ts +79 -0
- package/dist/contract/src/service.d.ts +806 -0
- package/dist/contract/src/ui.d.ts +1039 -0
- package/dist/contract/src/usage.d.ts +102 -0
- package/dist/core/llm/ai/anthropic-messages-request.d.ts +82 -0
- package/dist/core/llm/ai/anthropic-messages.d.ts +43 -0
- package/dist/core/llm/ai/cache-routing.d.ts +53 -0
- package/dist/core/llm/ai/chat-delta.d.ts +107 -0
- package/dist/core/llm/ai/compat-key.d.ts +19 -0
- package/dist/core/llm/ai/endpoint.d.ts +57 -0
- package/dist/core/llm/ai/failure.d.ts +181 -0
- package/dist/core/llm/ai/index.d.ts +16 -0
- package/dist/core/llm/ai/openai-chat-completions-request.d.ts +49 -0
- package/dist/core/llm/ai/openai-chat-completions.d.ts +37 -0
- package/dist/core/llm/ai/openai-responses-request.d.ts +63 -0
- package/dist/core/llm/ai/openai-responses.d.ts +11 -0
- package/dist/core/llm/ai/prefix-fingerprint.d.ts +20 -0
- package/dist/core/llm/ai/reasoning-compat.d.ts +69 -0
- package/dist/core/llm/ai/request-params-compat.d.ts +39 -0
- package/dist/core/llm/ai/retry.d.ts +130 -0
- package/dist/core/llm/ai/sanitize-history.d.ts +23 -0
- package/dist/core/llm/ai/think-tag.d.ts +37 -0
- package/dist/core/llm/ai/thinking-options.d.ts +35 -0
- package/dist/core/llm/ai/tool-names.d.ts +17 -0
- package/dist/core/llm/ai/tool-pairing-compat.d.ts +53 -0
- package/dist/core/llm/ai/tool-result-images-compat.d.ts +24 -0
- package/dist/core/llm/ai/tool-schema.d.ts +20 -0
- package/dist/core/llm/ai/usage-fields.d.ts +30 -0
- package/dist/core/llm/ai/vendors/index.d.ts +20 -0
- package/dist/core/llm/ai/vendors/thinking.d.ts +39 -0
- package/dist/core/llm/ai/vendors/types.d.ts +47 -0
- package/dist/core/llm/config/image-models.d.ts +34 -0
- package/dist/core/llm/config/retry-policy.d.ts +35 -0
- package/dist/core/llm/images/chat-images.d.ts +11 -0
- package/dist/core/llm/images/dimensions.d.ts +15 -0
- package/dist/core/llm/images/generate.d.ts +15 -0
- package/dist/core/llm/images/openai-images.d.ts +11 -0
- package/dist/core/llm/images/quota.d.ts +9 -0
- package/dist/core/llm/images/shape.d.ts +44 -0
- package/dist/core/llm/images/types.d.ts +118 -0
- package/dist/core/llm/model-catalog-format.d.ts +50 -0
- package/dist/core/llm/model-catalog.d.ts +51 -0
- package/dist/core/llm/types/message.d.ts +450 -0
- package/dist/core/llm/types/provider.d.ts +169 -0
- package/dist/core/llm/types/tool.d.ts +23 -0
- package/dist/core/llm/types.d.ts +10 -0
- package/dist/core/llm/utils/pricing.d.ts +18 -0
- package/dist/core/llm/utils/record.d.ts +2 -0
- package/dist/core/llm/utils/sse.d.ts +69 -0
- package/dist/infra/rpc/src/endpoint.d.ts +36 -0
- package/dist/infra/rpc/src/index.d.ts +2 -0
- package/dist/infra/rpc/src/rpc.d.ts +54 -0
- package/dist/theme.css +336 -0
- package/dist/ui.d.ts +2 -0
- package/lib/build.mjs +143 -0
- package/lib/check.mjs +23 -0
- package/lib/dev.mjs +52 -0
- package/lib/icons.mjs +2 -0
- package/lib/init.mjs +109 -0
- package/lib/install.mjs +80 -0
- package/lib/link.mjs +44 -0
- package/lib/lint/i18n.mjs +149 -0
- package/lib/lint/imports.mjs +83 -0
- package/lib/lint/index.mjs +54 -0
- package/lib/lint/styles.mjs +160 -0
- package/lib/manifest.d.mts +9 -0
- package/lib/manifest.mjs +99 -0
- package/lib/targets.mjs +60 -0
- package/package.json +34 -4
- package/skill/SKILL.md +150 -0
- package/skill/references/components.md +2830 -0
- package/skill/references/logic.md +526 -0
- package/skill/references/style-i18n.md +70 -0
- package/skill/references/ui.md +532 -0
- package/template/AGENTS.md +17 -0
- package/template/CLAUDE.md +1 -0
- package/template/README.md +12 -0
- package/template/gitignore +2 -0
- package/template/src/index.ts +28 -0
- package/template/src/messages.ts +16 -0
- package/template/src/types.ts +8 -0
- package/template/src/ui.tsx +30 -0
- package/template/tsconfig.json +7 -0
- package/tsconfig.json +25 -0
- package/README.md +0 -3
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 使用统计(插件经 `ctx.model.usage()` 读)和存储页从 agent 进程拿到的数据。
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 一小时里、一个供应商的一个模型发出的那些调用,合在一起。页面按它切区间、筛选、拆成柱,逐条的流水不出 agent 进程。
|
|
6
|
+
* 来自会话库的花销表:删掉会话不影响这些数。
|
|
7
|
+
*/
|
|
8
|
+
export interface UsageRequestBucket {
|
|
9
|
+
/** `YYYY-MM-DD HH`,本地时间。 */
|
|
10
|
+
hour: string;
|
|
11
|
+
/** 供应商 id。 */
|
|
12
|
+
provider: string;
|
|
13
|
+
model: string;
|
|
14
|
+
requests: number;
|
|
15
|
+
input: number;
|
|
16
|
+
output: number;
|
|
17
|
+
cacheRead: number;
|
|
18
|
+
cacheWrite: number;
|
|
19
|
+
/** 按调用当时记下的价格算的美元。 */
|
|
20
|
+
cost: number;
|
|
21
|
+
/** 查不到价的请求数:页面上的费用因此只能写「≥」。 */
|
|
22
|
+
unpriced: number;
|
|
23
|
+
/** 量得出生成速度的请求数,以及它们的输出 token 与生成耗时之和。 */
|
|
24
|
+
timed: number;
|
|
25
|
+
timedOutput: number;
|
|
26
|
+
streamMs: number;
|
|
27
|
+
/** 量得出首字时间的请求数与总毫秒。 */
|
|
28
|
+
firstTokens: number;
|
|
29
|
+
firstTokenMs: number;
|
|
30
|
+
/** 这些请求计费的 token(输入含缓存读写)按来源分摊,加起来是它们账单上的总数。各部分是估的比例乘实数。 */
|
|
31
|
+
parts: Record<UsageTokenPart, number>;
|
|
32
|
+
/** 发这些请求的会话 id,子会话记成它的顶层会话;已经删掉的不在。跨小时、跨模型数会话要去重,所以给 id 不给个数。 */
|
|
33
|
+
sessions: string[];
|
|
34
|
+
/** 人发的话(不含界面替人说的),记在它之后的第一次请求上。 */
|
|
35
|
+
turns: number;
|
|
36
|
+
/** 有计时的请求数,和它们从发出到收完、其中生成、生成里思考块各占的毫秒。等首字 = modelMs − generateMs。 */
|
|
37
|
+
timedReplies: number;
|
|
38
|
+
modelMs: number;
|
|
39
|
+
generateMs: number;
|
|
40
|
+
thinkingMs: number;
|
|
41
|
+
/** 工具调用次数、出错次数和执行毫秒(并行的各算各的),记在调完工具之后的那次请求上。 */
|
|
42
|
+
tools: number;
|
|
43
|
+
toolErrors: number;
|
|
44
|
+
toolMs: number;
|
|
45
|
+
/** 本该从缓存读到却按全价算的输入。 */
|
|
46
|
+
cacheMiss: UsageCacheMiss;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 计费 token 的来源:送进去的八类(系统提示词按段分出项目说明与记忆、技能与智能体,加上工具定义和四类消息)和吐出来的两类。
|
|
50
|
+
* `injected` 是界面或插件替人说的话(`synthetic` 的用户消息)。
|
|
51
|
+
*/
|
|
52
|
+
export type UsageTokenPart = "system" | "instructions" | "skills" | "toolDefs" | "user" | "injected" | "assistant" | "toolResults" | "outputThinking" | "outputReply";
|
|
53
|
+
/**
|
|
54
|
+
* 缓存未命中的原因,按同一会话相邻两次请求比出来:工具定义、系统提示词变了;已发出的消息被改写;前缀没变服务商没读到;
|
|
55
|
+
* 没有前缀记录分不清(重启后的第一次请求);压缩、撤回之后有意重写;换了模型或服务商。
|
|
56
|
+
*/
|
|
57
|
+
export type UsageCacheMissCause = "tools" | "prompt" | "rewrite" | "provider" | "unknown" | "compaction" | "rewind" | "model";
|
|
58
|
+
export interface UsageCacheMiss {
|
|
59
|
+
tokens: number;
|
|
60
|
+
/** 按请求当时记下的费率,这些 token 比按缓存读取多花的美元。 */
|
|
61
|
+
cost: number;
|
|
62
|
+
/** 没记费率、算不出多花多少的那部分 token。 */
|
|
63
|
+
unpriced: number;
|
|
64
|
+
byCause: Partial<Record<UsageCacheMissCause, number>>;
|
|
65
|
+
}
|
|
66
|
+
export interface UsageScan {
|
|
67
|
+
requests: UsageRequestBucket[];
|
|
68
|
+
/** 供应商 id → 现在的名字。花销按 id 记,删掉的供应商不在这里,只剩 id。 */
|
|
69
|
+
providerNames: Record<string, string>;
|
|
70
|
+
/** 已归档的顶层会话 id,和 `UsageRequestBucket.sessions` 对着数归档了几个。 */
|
|
71
|
+
archived: string[];
|
|
72
|
+
}
|
|
73
|
+
/** 一天里最后活动过的那些会话:几条,占多少。 */
|
|
74
|
+
export interface StorageDay {
|
|
75
|
+
day: string;
|
|
76
|
+
sessions: number;
|
|
77
|
+
bytes: number;
|
|
78
|
+
}
|
|
79
|
+
export interface StorageUse {
|
|
80
|
+
/** 会话记录占的字节数。 */
|
|
81
|
+
bytes: number;
|
|
82
|
+
/** 有几条会话。 */
|
|
83
|
+
sessions: number;
|
|
84
|
+
/** 最早、最晚那条会话的最后活动日(本地 `YYYY-MM-DD`)。没有会话时都是 null。 */
|
|
85
|
+
earliest: string | null;
|
|
86
|
+
latest: string | null;
|
|
87
|
+
/** 按最后活动日摊开:选了一段日子会删掉什么,按下去之前就算得出来,确认框不会拿总数去问。 */
|
|
88
|
+
days: StorageDay[];
|
|
89
|
+
}
|
|
90
|
+
/** 要删哪一段,两头都含当天(本地 `YYYY-MM-DD`);空的那头不设限。 */
|
|
91
|
+
export interface ClearRange {
|
|
92
|
+
from: string | null;
|
|
93
|
+
to: string | null;
|
|
94
|
+
}
|
|
95
|
+
export interface ClearResult {
|
|
96
|
+
/** 删掉了几条会话。 */
|
|
97
|
+
removed: number;
|
|
98
|
+
/** 释放了多少字节(按删之前量到的记录大小算)。 */
|
|
99
|
+
freed: number;
|
|
100
|
+
/** 正在跑、所以没动的那几条。 */
|
|
101
|
+
skipped: number;
|
|
102
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Our messages, in the shape the Messages API wants.
|
|
3
|
+
*
|
|
4
|
+
* A translation and nothing else. Anthropic's shape differs from ours in two ways that matter and
|
|
5
|
+
* are easy to get wrong: tool results are user-role blocks rather than their own role, and a
|
|
6
|
+
* thinking block has to be replayed with a `signature` field — though **whether that signature has
|
|
7
|
+
* to verify depends on the endpoint**, which is why `thinkingReplay` is a parameter and not a
|
|
8
|
+
* constant. See `thinkingReplay` in `anthropic-messages.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import type { Message, ToolSpec } from "../types.ts";
|
|
11
|
+
/**
|
|
12
|
+
* One block of content, in the wire shape.
|
|
13
|
+
*
|
|
14
|
+
* Open-ended on purpose: the API keeps adding block kinds, and a closed type would mean this file
|
|
15
|
+
* has to be edited before a new one can be passed through untouched.
|
|
16
|
+
*/
|
|
17
|
+
interface AnthropicBlock {
|
|
18
|
+
type: string;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
/** One message in the wire shape. Exported because the caller assembles a request around it. */
|
|
22
|
+
export interface AnthropicMessage {
|
|
23
|
+
role: "user" | "assistant";
|
|
24
|
+
content: AnthropicBlock[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 无签名的思考块在下一轮请求里的去向。
|
|
28
|
+
*
|
|
29
|
+
* 三档,从发得最多到发得最少。**没有一档对所有端点都对**——这正是它是参数的原因,默认值由
|
|
30
|
+
* `anthropic-messages.ts` 按端点算,撞了再往下走一格。
|
|
31
|
+
*
|
|
32
|
+
* - `"unsigned"`:连没签名的也回放,送 `signature: ""`。非签名端点(DeepSeek / Z.AI / Moonshot
|
|
33
|
+
* 的 Anthropic 兼容端点)上的推理模型要的是这一档:它们不签名,但要求把推理带回来。
|
|
34
|
+
* - `"signed-only"`:只发带签名的,没签名的整块丢掉。官方 `api.anthropic.com` 要的是这一档——它
|
|
35
|
+
* 会验签,`signature: ""` 过不去。
|
|
36
|
+
* - `"none"`:一块都不发。签名是别的部署签的、连剥都救不回来时的最后一格。
|
|
37
|
+
*
|
|
38
|
+
* 丢掉而不是降级成文本块,是刻意的:官方 Anthropic 上把推理改写成普通文本会撞上
|
|
39
|
+
* reasoning_extraction 分类器。
|
|
40
|
+
*/
|
|
41
|
+
export type ThinkingReplay = "unsigned" | "signed-only" | "none";
|
|
42
|
+
export interface AnthropicEncodeOptions {
|
|
43
|
+
/** 无签名思考块的去向。默认 `"signed-only"`——官方端点的行为,也是这个文件一直以来的行为。 */
|
|
44
|
+
thinkingReplay?: ThinkingReplay;
|
|
45
|
+
/** 这个模型收不收图片。默认 `true`。 */
|
|
46
|
+
supportsImages?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* 在消息尾部滚动几个提示缓存断点。默认 0。
|
|
49
|
+
*
|
|
50
|
+
* Anthropic 一个请求总共只给 4 个断点(官方文档:`Max 4 cache_control breakpoints per
|
|
51
|
+
* request`),system 和工具列表各占一个,所以这里最多 2。
|
|
52
|
+
*/
|
|
53
|
+
cacheBreakpoints?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Anthropic requires tool results to arrive as `tool_result` blocks inside a *user* message,
|
|
57
|
+
* and consecutive results must be merged into one message. Our flat message list has one
|
|
58
|
+
* entry per result, so this collapses runs of them.
|
|
59
|
+
*
|
|
60
|
+
* The merged blocks are put back into the order the calls were made, which is not the order the
|
|
61
|
+
* results were recorded in: tools run in parallel and each result is written down as it finishes,
|
|
62
|
+
* so a history rebuilt from the log has the quickest tool first. Anthropic matches by
|
|
63
|
+
* `tool_use_id` and accepts either, but the request should not differ depending on which tool won
|
|
64
|
+
* the race — an identical conversation that serialises two ways defeats prompt caching for
|
|
65
|
+
* everything after it.
|
|
66
|
+
*/
|
|
67
|
+
export declare function toAnthropicMessages(messages: Message[], options?: AnthropicEncodeOptions): AnthropicMessage[];
|
|
68
|
+
/**
|
|
69
|
+
* Anthropic 收的图片就这四种。
|
|
70
|
+
*
|
|
71
|
+
* 不在表里的换成文字,而不是原样透传:`tools/paths.ts` 的 `IMAGE_EXTENSIONS` 里有 `.bmp`,MCP 服务器
|
|
72
|
+
* 也可以给回任意 mimeType,而**那个 tool_result 一旦进了历史,之后每一轮都带着它**——透传换来的不是
|
|
73
|
+
* 一次失败,是一个再也说不了话的会话。
|
|
74
|
+
*
|
|
75
|
+
* `image/jpg` 是个常见的写法,改成 `image/jpeg` 而不是判死刑。
|
|
76
|
+
*/
|
|
77
|
+
export declare function normalizeImageMediaType(mimeType: string): string | undefined;
|
|
78
|
+
/** 同上,但走遍一个任意深度的值。没有任何一处需要改时,返回的是**原来那个对象**。 */
|
|
79
|
+
export declare function wellFormedDeep(value: unknown): unknown;
|
|
80
|
+
export declare function toAnthropicTools(tools: ToolSpec[]): unknown[];
|
|
81
|
+
export declare function normalizeToolSchema(schema: unknown): unknown;
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Messages API adapter.
|
|
3
|
+
*
|
|
4
|
+
* Maps the neutral message model onto `/v1/messages` with `stream: true`,
|
|
5
|
+
* including extended thinking and its signature round-trip.
|
|
6
|
+
*/
|
|
7
|
+
import { type ThinkingReplay } from "./anthropic-messages-request.ts";
|
|
8
|
+
import type { ModelConfig, Provider, ProviderConfig, RequestOptions } from "../types.ts";
|
|
9
|
+
export declare const anthropicMessagesProvider: Provider;
|
|
10
|
+
/**
|
|
11
|
+
* 这个地址上的端点会验思考签名吗。
|
|
12
|
+
*
|
|
13
|
+
* 官方那条必须**精确**匹配 origin 或者后面紧跟一个 `/`——前缀匹配会把
|
|
14
|
+
* `https://api.anthropic.com.evil.com` 当成官方。空地址按官方算:那是配置没填好,而按官方算等于保持
|
|
15
|
+
* 现状,是两个方向里安全的那个。
|
|
16
|
+
*
|
|
17
|
+
* 其余几个是把 Claude 转在自己身份后面、但照样执行 Anthropic 签名协议的网关。
|
|
18
|
+
*/
|
|
19
|
+
export declare function signsThinkingSignatures(baseUrl: string | undefined): boolean;
|
|
20
|
+
/** 这个模型现在该发哪一档。给了会话(`cacheKey`)时,那个会话自己学到的优先。 */
|
|
21
|
+
export declare function thinkingReplay(provider: ProviderConfig, model: ModelConfig, session?: string): ThinkingReplay;
|
|
22
|
+
/**
|
|
23
|
+
* 「你这个签名我不认」的三种说法——往下走一格。返回「结论变了,值得换个形状重发」。
|
|
24
|
+
*
|
|
25
|
+
* 第一条是官方 Anthropic 和挡在它前面的中转的原话(`400 Invalid \`signature\` in \`thinking\` block`);
|
|
26
|
+
* 第二条是 Bedrock 系中转的——空串在它那边先过不了 schema 校验,于是报成
|
|
27
|
+
* `messages.N.content.M.thinking.signature: Field required`。第三条是官方对「最新那条助手消息里的思考块
|
|
28
|
+
* 和它当初返回的不一样」的原话(`` `thinking` or `redacted_thinking` blocks in the latest assistant message
|
|
29
|
+
* cannot be modified ``)。
|
|
30
|
+
*
|
|
31
|
+
* `[^"\n]{0,32}` 这一段是刻意收紧的:这样它只匹配同一条错误信息里紧挨着的那句,不会跨过一个引号或者
|
|
32
|
+
* 换行去捞另一件事的 `required`。
|
|
33
|
+
*
|
|
34
|
+
* 只认这三条。多认一条的代价是把别的原因造成的 400 误判成签名问题,然后拿一个改坏了的请求去重发。第三条
|
|
35
|
+
* 加得进来,是因为它点名的就是思考块,而补救方向一致:思考块发得越少越不会撞上它。
|
|
36
|
+
*
|
|
37
|
+
* 这一个直接改进程级的表,流里不走它——流里的降级先只算这一次请求,见文件这一段开头。
|
|
38
|
+
*/
|
|
39
|
+
export declare function learnThinkingReplay(providerId: string, modelId: string, error: string, from?: ThinkingReplay): boolean;
|
|
40
|
+
/** 测试用:把学到的都忘掉。 */
|
|
41
|
+
export declare function resetThinkingReplay(): void;
|
|
42
|
+
/** 这一份请求该带哪些采样参数。见 `THINKING_FORBIDS_SAMPLING`。 */
|
|
43
|
+
export declare function samplingFor(thinkingEnabled: boolean, model: Pick<ModelConfig, "samplingParams">, options: Pick<RequestOptions, "temperature" | "samplingParams">): Record<string, unknown>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 缓存路由键(`RequestOptions.cacheKey`)怎么带给服务商。
|
|
3
|
+
*
|
|
4
|
+
* 缓存命中是按机器算的。同一条对话前缀的请求被负载均衡打散到别的机器、号池中转换了一个上游账号,
|
|
5
|
+
* 前缀再一样也是全价重算。路由键就是告诉服务商「这几次请求是同一条对话,送到同一处」。
|
|
6
|
+
*
|
|
7
|
+
* 不按端点区分,每个请求都带同一套,一共这几项:
|
|
8
|
+
*
|
|
9
|
+
* - OpenAI 系两条链:请求头 `x-session-id`(OpenRouter 的粘性路由键)、
|
|
10
|
+
* 请求头 `session_id`(Codex CLI 发的;sub2api 一类号池选账号先看它,其次 `conversation_id` 头和
|
|
11
|
+
* `prompt_cache_key`,都没有就随机分,见 Wei-Shaw/sub2api#1421)、请求体 `prompt_cache_key`
|
|
12
|
+
* (OpenAI 官方字段,Kimi、通用中转也认)。未知请求头各家都是忽略;严格端点对未知的请求体字段
|
|
13
|
+
* 会 400 并点名它,撞一次就学会不发(`request-params-compat.ts` 的 `cache-key`),两个头照带。
|
|
14
|
+
* - Anthropic 协议:请求体 `metadata.user_id`,写成 Claude Code 的 JSON,
|
|
15
|
+
* Claude 号池类中转按其中的 `session_id` 粘住账号。缓存本身仍由
|
|
16
|
+
* `cache_control` 断点声明。
|
|
17
|
+
*
|
|
18
|
+
* 端点硬性要求的会话头(例如 OpenCode Go 的 `x-opencode-session`,缺了直接 400)另见 `sessionHeaders`:
|
|
19
|
+
* 三种协议都带,没有 `cacheKey` 时也带。
|
|
20
|
+
*/
|
|
21
|
+
import type { DroppedParam } from "./request-params-compat.ts";
|
|
22
|
+
/** 这次请求要加到请求体和请求头里的东西。 */
|
|
23
|
+
export interface CacheRouting {
|
|
24
|
+
body: Record<string, string>;
|
|
25
|
+
headers: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 键放得进这个位置就原样用;放不进(超长,或有请求头容不下的字符)就压成「可读前缀-摘要」。
|
|
29
|
+
*
|
|
30
|
+
* 不照抄 pi 的截断:主会话 `<会话 id>` 和子代理 `<会话 id>-sub-<n>` 这类共享长前缀的键,截到 64 字符
|
|
31
|
+
* 后会变成同一个,子代理和主会话被路由成同一条对话。摘要取的是整条键,区分得开。
|
|
32
|
+
* 请求头的值必须是可见 ASCII,否则 `fetch` 直接抛 TypeError——整个请求发不出去。
|
|
33
|
+
*/
|
|
34
|
+
export declare function fitKey(key: string, maxLength: number): string;
|
|
35
|
+
/**
|
|
36
|
+
* OpenAI 系两条链这次该带什么。没有 `cacheKey` 返回空;请求体字段被这个模型拒过就只带请求头。
|
|
37
|
+
*/
|
|
38
|
+
export declare function cacheRouting(cacheKey: string | undefined, dropped: ReadonlySet<DroppedParam>): CacheRouting;
|
|
39
|
+
/** Anthropic 协议这次该在请求体里加什么。没有 `cacheKey` 返回空,请求和从前一样。 */
|
|
40
|
+
export declare function anthropicMetadata(cacheKey: string | undefined): {
|
|
41
|
+
metadata?: {
|
|
42
|
+
user_id: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* 端点硬性要求的会话头。三种协议都走这里,Anthropic 协议也不例外——
|
|
47
|
+
* 这不是可选的路由提示,缺了请求就发不出去。
|
|
48
|
+
*
|
|
49
|
+
* 目前只有 OpenCode Go 的 `x-opencode-session`(缺了直接 400),写死。
|
|
50
|
+
* 没有 `cacheKey`(压缩、测试连接这类一次性请求)时换成一个随机 id 而不是不带:随机 id 只是失去路由,
|
|
51
|
+
* 请求照样能发。调用方每次请求算一次,重试沿用同一个结果。
|
|
52
|
+
*/
|
|
53
|
+
export declare function sessionHeaders(baseUrl: string, cacheKey: string | undefined): Record<string, string>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Completions 流的入站解码:一个 chunk 里的各个字段各是什么意思。
|
|
3
|
+
*
|
|
4
|
+
* 这里全是一帧看一帧、不碰网络的判断(跨 chunk 的 `<think>` 切分在 `think-tag.ts`)——各家把同一件事
|
|
5
|
+
* 写成不同的形状,认形状的规则住在这里,`openai-chat-completions.ts` 只管把它们接进一条流的状态里。
|
|
6
|
+
*/
|
|
7
|
+
import type { StopReason } from "../types.ts";
|
|
8
|
+
/** 这个 delta 里的推理文本,和它挂着的那个键。三个都看,取第一个非空的。 */
|
|
9
|
+
export declare function reasoningFromDelta(delta: Record<string, unknown>): {
|
|
10
|
+
field: string;
|
|
11
|
+
text: string;
|
|
12
|
+
} | undefined;
|
|
13
|
+
/** 把泄漏的特殊 token 去掉。首尾被剥掉的那一个连带它旁边的空白一起去,免得留下一行空格。 */
|
|
14
|
+
export declare function stripDeepseekTokens(text: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* 结尾那个还没闭合的 token,扣下来等下一个 chunk。
|
|
17
|
+
*
|
|
18
|
+
* 一个 token 会被 SSE 切成两半(`<|tool` / `▁calls▁begin|>`),分开看两半都不匹配。末尾单独一个 `<`
|
|
19
|
+
* 也扣着——它可能是下一个 token 的开头。扣留长度设上限,免得正文里一个孤零零的 `<|` 让缓冲无限长。
|
|
20
|
+
*/
|
|
21
|
+
export declare function trailingPartialDeepseekToken(text: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* `finish_reason` 说的是什么。认不出来的,名字里带着出错的意思才算失败。
|
|
24
|
+
*
|
|
25
|
+
* 从前只认 `stop` / `tool_calls` / `length` 三个,其余留在 `pending`,最后被兜成 `stop` 或 `toolUse`
|
|
26
|
+
* ——**上游中途挂掉报 `finish_reason: "error"`,我们判成功**,把截断的半截答案交给 agent loop 接着跑。
|
|
27
|
+
* 用户看到的是无声截断:没有错误,没有重试,就是话说到一半。
|
|
28
|
+
*
|
|
29
|
+
* 映射里每一条的来历:
|
|
30
|
+
*
|
|
31
|
+
* - 先折大小写:网关顶着 Google Gemini 后端时会发原生的大写 `STOP` / `MAX_TOKENS`。
|
|
32
|
+
* - `error`:网关把上游模型的故障报成一个
|
|
33
|
+
* 不带细节的裸 `error`,多数是临时的。
|
|
34
|
+
* - `insufficient_system_resource`:DeepSeek 自己的文档说这是推理系统资源不足导致的
|
|
35
|
+
* 中断,属于服务端容量问题。
|
|
36
|
+
* - `content_filter`:内容策略。
|
|
37
|
+
*
|
|
38
|
+
* 后来补的几家:TGI 的 `eos_token` / `stop_sequence` 是正常说完,Mistral 的 `model_length` 是撞上长度上限,
|
|
39
|
+
* 智谱的 `sensitive` 是内容审核。
|
|
40
|
+
*
|
|
41
|
+
* **认不出来的不再一律当失败**。一律当失败的规则要防的是上面 `error` 那种被判成成功的无声截断,而它的代价落在另一头:一个只是**说法不同**的正常结束(上面这几家
|
|
42
|
+
* 补进来之前就是这样)每次都被作废、重试,重试拿到的还是同一个说法——同一家端点上**每一轮都失败**,
|
|
43
|
+
* 用户手上没有任何办法绕过去。两头都会错的时候,看名字:带着出错、中断、超限意思的仍然是失败(防的那
|
|
44
|
+
* 件事照旧防住),像内容审核的落进内容策略,剩下的按说完了交付。
|
|
45
|
+
*
|
|
46
|
+
* 返回 `stopReason` 表示这是一个正常的结束;返回 `error` 表示要抛,抛什么由调用方交给 `failure.ts` 判。
|
|
47
|
+
*/
|
|
48
|
+
export declare function mapFinishReason(reason: string): {
|
|
49
|
+
stopReason?: StopReason;
|
|
50
|
+
error?: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* 一个 delta 的 `content` 里,正文和推理各是什么。
|
|
54
|
+
*
|
|
55
|
+
* 协议写的是字符串,Mistral(Medium、Magistral)发的是**内容片段数组**:`[{type:"text",text}]`,推理模型
|
|
56
|
+
* 还有 `{type:"thinking",thinking:[{type:"text",text}]}`。从前直接 `+=`,数组被转成 `[object Object]`
|
|
57
|
+
* 写进正文、进了历史。
|
|
58
|
+
*
|
|
59
|
+
* 认不出来的片段一律丢掉,不转成字符串——宁可少一段,不能让对象的字面写法进正文。
|
|
60
|
+
*/
|
|
61
|
+
export declare function contentOfDelta(content: unknown): {
|
|
62
|
+
text: string;
|
|
63
|
+
thinking: string;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* 这一帧是不是一个错误,是的话它说了什么。
|
|
67
|
+
*
|
|
68
|
+
* 从前只认 `error` 且**没有** `choices` 的帧。OpenRouter 的流内错误偏偏两样都带:顶层
|
|
69
|
+
* `error: {code, message}`,外加 `choices: [{delta: {content: ""}, finish_reason: "error"}]`——于是错误被
|
|
70
|
+
* 放过去,落到 `finish_reason: "error"` 上,原话丢了,只剩一句「上游报告这次生成失败」去重试:额度不足、
|
|
71
|
+
* 上下文超长都认不出来,后者本该触发压缩。`{error, choices: []}` 更糟,落成一条空回答。
|
|
72
|
+
*
|
|
73
|
+
* 所以:`error` 带着一句话(字符串,或对象上的 `message`)就是错误,不看 `choices`;什么都没说的
|
|
74
|
+
* `error` 只在没有可读的 `choices` 时才算——那是这里原来的判据,不收紧。
|
|
75
|
+
*
|
|
76
|
+
* 不写 `error` 的错误帧也有好几种,从前都落成空回答:SSE 自己的 `event: error`(`frameEvent`)、国内中转
|
|
77
|
+
* 爱用的 `{code: 500, msg}` 和 `{success: false}`。后两种只在没有 `choices` 时才算:正常帧也有带着 `code: 0` 的,而真正的错误帧
|
|
78
|
+
* 不会再带一份回答;`code` 还得是个错误码,见 `isErrorCode`。没给原话的返回 `{}`,分类器会从整帧原文里自己找。
|
|
79
|
+
*/
|
|
80
|
+
export declare function streamErrorOf(event: Record<string, unknown>, frameEvent?: string): {
|
|
81
|
+
message?: string;
|
|
82
|
+
} | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* 这一帧的用量。顶层没有就看 `choices[0].usage`。
|
|
85
|
+
*
|
|
86
|
+
* Kimi / Moonshot 把 usage 放在 choice 里,从前只读顶层,这几家的花费全记成 0。
|
|
87
|
+
*/
|
|
88
|
+
export declare function usageOf(event: Record<string, unknown>, choice: Record<string, unknown> | undefined): object | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* 一个工具调用的参数,接上这一片之后是什么。
|
|
91
|
+
*
|
|
92
|
+
* 协议写的是「字符串分片,按顺序拼」。实际有三种别的发法:
|
|
93
|
+
*
|
|
94
|
+
* - **字符串,但收尾又重发了一遍整串**:先给完整的 `{"path":"a"}`,在带 `finish_reason` 的那一片里再给
|
|
95
|
+
* 一次。拼起来是两份,解析失败,工具拒绝执行。判据:已攒的是一段完整 JSON,新来的也是完整 JSON,并且
|
|
96
|
+
* 等于已攒的或以它开头——增量流里一段完整 JSON 后面不会再接一段以它整个开头的东西,所以只可能是快照。
|
|
97
|
+
* - **对象,每片带一部分键**:`{path:"a"}` 再 `{offset:10}`。从前按「对象就是整份参数」覆盖,前面的
|
|
98
|
+
* 键丢了。改成合并。浅合并就够:见过的分片都是按顶层键分的,没见过把一个键的值拆成两半发的;真要拆,
|
|
99
|
+
* 嵌套对象被后一片整个替换,和从前的覆盖是同一个结果,不会更坏。
|
|
100
|
+
* - **对象,每片重发同一整份**:合并同一份等于它自己,行为不变。
|
|
101
|
+
*
|
|
102
|
+
* 对象碰上 `+` 会变成 `[object Object]`,所以序列化走 `argumentFragment`。
|
|
103
|
+
*/
|
|
104
|
+
export declare function nextArguments(text: string, object: Record<string, unknown> | undefined, raw: unknown): {
|
|
105
|
+
text: string;
|
|
106
|
+
object?: Record<string, unknown>;
|
|
107
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「撞出来之后记住」的那几张表(`reasoning-compat.ts`、`tool-pairing-compat.ts`、
|
|
3
|
+
* `request-params-compat.ts`、Anthropic 链的思考回放/思考形状、Chat 链的输出上限字段名)共用的键。
|
|
4
|
+
*
|
|
5
|
+
* 学和查必须用同一个键,否则学到的结论永远查不到:Responses 链曾经查排列时用 `model.modelId`、
|
|
6
|
+
* 学的时候用 `model.id`,于是撞一次学一次、下一次请求照旧按默认形状发,同一个 400 每轮都来一遍。
|
|
7
|
+
* 两边各自拼字符串迟早会再漂一次,所以键只在这里拼,模型那一半也只从这里取。
|
|
8
|
+
*
|
|
9
|
+
* 取 `model.id`(配置里的唯一 id)而不是 `model.modelId`(线上模型名):同一个服务商下可以配两条
|
|
10
|
+
* 指向同一个线上模型、参数不同的配置,它们该各学各的。
|
|
11
|
+
*/
|
|
12
|
+
import type { ModelConfig, ProviderConfig } from "../types.ts";
|
|
13
|
+
/** 学习表的键。所有 learnXxx / 查表函数内部都只用它。 */
|
|
14
|
+
export declare const compatKey: (providerId: string, modelId: string) => string;
|
|
15
|
+
/** 适配器调 learnXxx / 查表函数时传的那一对 id。三条链都从这里取,不自己挑字段。 */
|
|
16
|
+
export declare function compatScope(provider: Pick<ProviderConfig, "id">, model: Pick<ModelConfig, "id">): {
|
|
17
|
+
providerId: string;
|
|
18
|
+
modelId: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 三条链都要做、而且必须做得一样的几件小事:拼地址、带上同一个 User-Agent、把 fetch 的失败说成人话。
|
|
3
|
+
*
|
|
4
|
+
* 它们原来住在 `anthropic-messages.ts` 里,被另外两条链跨适配器 import——也就是说「OpenAI 那条链
|
|
5
|
+
* 的 URL 怎么拼」这件事的答案,写在 Anthropic 那个文件的第 619 行。审计把这一条列在「三条链共用
|
|
6
|
+
* 零件、不共用骨架」下面,理由不是体积:一个住在某一条链里的共享零件,改它的时候看不见另外两个
|
|
7
|
+
* 使用者,而这两个函数恰好都是「错了不会报错、只会行为变怪」的那种。
|
|
8
|
+
*
|
|
9
|
+
* `joinUrl` 的那条 `/v1` 规则是真事故留下的:用户把 baseUrl 填成 `https://host/v1`,而适配器又拼
|
|
10
|
+
* 上 `/v1/messages`,得到 `/v1/v1/messages`。三条链都拼地址,所以三条链都需要这条规则——放在这里
|
|
11
|
+
* 之后,它是一个大家都看得见的决定,而不是某条链的私事。
|
|
12
|
+
*/
|
|
13
|
+
import type { AssistantMessage, ModelConfig, StreamEvent, Usage } from "../types.ts";
|
|
14
|
+
/** 模型请求的 User-Agent。不写的话 Node 的 fetch 会自己填 `node`,端点那边认不出是谁在调。 */
|
|
15
|
+
export declare const USER_AGENT = "Plume";
|
|
16
|
+
/** `base` 与 `path` 拼成一个地址,避开 `/v1` 重复。 */
|
|
17
|
+
export declare function joinUrl(base: string, path: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* 一次尝试的用量,算好合计和费用。每次尝试各算各的,再用 `addUsage` 相加。
|
|
20
|
+
*
|
|
21
|
+
* 不能先加总再计价:`selectPricingRates` 按「这一次请求的上下文有多长」挑长上下文档位,三次 150k
|
|
22
|
+
* 的尝试加起来是 450k,会被按 >200k 那一档整体计价——每一次都没到那个档,账却按那个档记。
|
|
23
|
+
* `addUsage` 本来就逐项相加费用,所以先各自 `computeCost` 再加,得到的正是每次各按自己的档位计价之和。
|
|
24
|
+
*/
|
|
25
|
+
export declare function priceAttempt(usage: Usage, model: ModelConfig): Usage;
|
|
26
|
+
/**
|
|
27
|
+
* Price the final attempt and fold in what earlier attempts cost.
|
|
28
|
+
*
|
|
29
|
+
* The final attempt is also kept on its own when there is anything to fold: the sum is the bill,
|
|
30
|
+
* but only this one request ever sat in the context window, and reading the sum as window size
|
|
31
|
+
* made a single retry look like twice the conversation. See `lastAttemptUsage`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function settleUsage(partial: AssistantMessage, model: ModelConfig, spentOnRetries: Usage): void;
|
|
34
|
+
/**
|
|
35
|
+
* 一次流失败或被中止,把它收成一条错误消息。
|
|
36
|
+
*
|
|
37
|
+
* 这 21 行原来在三条链里各抄一遍,逐字相同(只有一条链少了一行注释)。逐字重复的代价不是体积,
|
|
38
|
+
* 是漂移:审计记下的「三条链已经产生可观测分歧」全部出在这种地方——改一处忘两处,而三处看起来
|
|
39
|
+
* 都对。这一段管的是「失败时消息长什么样」,三条协议没有任何理由长得不一样。
|
|
40
|
+
*
|
|
41
|
+
* 成功那一侧**没有**抽出来,而且不该抽:`stopReason` 怎么算是真协议差异(Anthropic 有
|
|
42
|
+
* `stop_reason` 字段、chat/completions 靠 `finish_reason` 加工具调用推断、Responses 看收尾事件),
|
|
43
|
+
* 把三种算法塞进一个带 flag 的函数只会让每一条都更难读。
|
|
44
|
+
*
|
|
45
|
+
* 返回那个事件而不是 yield 它:这是个普通函数,调用方是生成器,`yield` 只能发生在生成器自己
|
|
46
|
+
* 那一层。
|
|
47
|
+
*/
|
|
48
|
+
export declare function failedStreamEvent(partial: AssistantMessage, context: {
|
|
49
|
+
error: unknown;
|
|
50
|
+
signal: AbortSignal | undefined;
|
|
51
|
+
model: ModelConfig;
|
|
52
|
+
/** 前几次失败尝试各自花掉的 token,**已经各自计过价**(`priceAttempt`),要算进账。 */
|
|
53
|
+
spentOnRetries: Usage;
|
|
54
|
+
startTime: number;
|
|
55
|
+
/** 第一个 token 到达的时刻,`null` 表示一个都没到。 */
|
|
56
|
+
firstTokenTime: number | null;
|
|
57
|
+
}): StreamEvent;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一次失败是什么、值不值得再试一次——只在这里回答一次。
|
|
3
|
+
*
|
|
4
|
+
* 从前这个判断被写了四遍:HTTP 状态码一份白名单(`isRetryableStatus`),传输异常一份白名单
|
|
5
|
+
* (`isRetryableError`),流内 error 事件干脆当成「服务商本人拒绝了」直接早退,整轮恢复那层再
|
|
6
|
+
* 拿一个 `retryable` 布尔自己判一次。四份名单互不知情,于是同一个故障在这一层算「可以再试」、
|
|
7
|
+
* 在下一层算「没救了」,设置页写着「无限重试」而请求在第一次就放弃——因为那种失败压根没进过
|
|
8
|
+
* 重试的门。
|
|
9
|
+
*
|
|
10
|
+
* 更要命的是四份都是**白名单**:只有见过的才重试。中转每换一种错法就多一个漏网的,而中转换错法
|
|
11
|
+
* 这件事是不会停的。所以这里把默认值翻过来:
|
|
12
|
+
*
|
|
13
|
+
* 只有明确「再问一百遍也是这个答复」的才是 `fatal`,其余一切都值得再试。
|
|
14
|
+
*
|
|
15
|
+
* 黑名单是有限的、确定的、几乎不增长的——密钥、模型名、请求体、配额、内容策略,就这几样。而
|
|
16
|
+
* 「其余一切」是敞开的:下一个没见过的状态码、下一种错误体格式、下一个只写着 `Unknown provider
|
|
17
|
+
* error` 的空事件,不改一行代码就自动落进 `upstream`。
|
|
18
|
+
*
|
|
19
|
+
* 两个可重试的值刻意只有两个,因为设置页上就是两行(`RetryPolicy`)。类型层面保证「界面上能配
|
|
20
|
+
* 的」和「实际会重试的」不会各说各话——想加第三类,改这个枚举,编译器会把该改的地方全指出来。
|
|
21
|
+
*/
|
|
22
|
+
type FailureKind = "network" | "upstream" | "fatal";
|
|
23
|
+
/**
|
|
24
|
+
* 界面据此挂动作:401 旁边给「去检查密钥」,404 给「换个模型」。
|
|
25
|
+
*
|
|
26
|
+
* 是分类的产物而不是界面自己猜的,所以加一个动作不用碰分类逻辑,认一种新错误也不用碰界面。
|
|
27
|
+
*/
|
|
28
|
+
type FailureHint = "check-key" | "check-model" | "check-billing" | "check-request" | "blocked";
|
|
29
|
+
/**
|
|
30
|
+
* 循环自己能处理的那几种 `fatal`,给程序认的名字。
|
|
31
|
+
*
|
|
32
|
+
* `hint` 是给界面挂动作的,粒度按「人该去改哪儿」分;这里按「循环能做什么」分,两者不重合:
|
|
33
|
+
* 上下文超长和请求体格式错误在界面上都是 `check-request`,而只有前者压缩一次历史就可能过。
|
|
34
|
+
*/
|
|
35
|
+
type FailureCode = "context-overflow";
|
|
36
|
+
export interface Failure {
|
|
37
|
+
kind: FailureKind;
|
|
38
|
+
/** 一句话,一行放得下,给人看的。 */
|
|
39
|
+
summary: string;
|
|
40
|
+
/** 服务商原文,展开或悬停才看。 */
|
|
41
|
+
detail?: string;
|
|
42
|
+
/** 服务器自己说的等待时间,比任何我们猜的曲线都准。 */
|
|
43
|
+
retryAfterMs?: number;
|
|
44
|
+
hint?: FailureHint;
|
|
45
|
+
/** 见 `FailureCode`。只在认出来时有,缺省不代表「不是」——旧日志里的失败都没有它。 */
|
|
46
|
+
code?: FailureCode;
|
|
47
|
+
/**
|
|
48
|
+
* 「还是刚才那个错误吗」的答案。
|
|
49
|
+
*
|
|
50
|
+
* 无限重试撞上一个我们不认识的终局错误时,界面唯一能说的真话就是「已经重连 47 次,一直是
|
|
51
|
+
* 同一个错误」。指纹让这句话不需要认识任何具体的错误就能说出口——真正的临时故障,文案和状态
|
|
52
|
+
* 码会变;一字不差重复几十次的,人一眼就知道不是在排队。
|
|
53
|
+
*/
|
|
54
|
+
fingerprint: string;
|
|
55
|
+
/**
|
|
56
|
+
* 这一种失败自己的重试上限,和用户的策略取更严的那个。
|
|
57
|
+
*
|
|
58
|
+
* 绝大多数失败不该有这个——「重试几次」是用户的决定,设置页上那个「一直重试」写着的就是
|
|
59
|
+
* 「网络回来之前别放弃」,那是他要的。
|
|
60
|
+
*
|
|
61
|
+
* 例外是重发根本不可能改变结果的那些。空回答是眼下唯一一种:重发的是逐字节相同的请求体,
|
|
62
|
+
* 拿回来的是逐字节相同的空。服务端状态的波动(503、限流、实例在换)几次之内就会变个样子,
|
|
63
|
+
* 而几十次一模一样的,只说明这个请求在这个端点上就是会得到空——再等一百次也是。
|
|
64
|
+
*
|
|
65
|
+
* 真实日志里量到过一次:同一个请求重发 222 次、34 分钟,直到人工按停。
|
|
66
|
+
*/
|
|
67
|
+
retryLimit?: number;
|
|
68
|
+
/**
|
|
69
|
+
* 这次失败有没有已经花掉 token。
|
|
70
|
+
*
|
|
71
|
+
* 流已经开始吐字,说明请求被受理了,那些 token 服务商已经收过钱——重试就是再付一次。连接在
|
|
72
|
+
* 握手阶段就断掉则一分钱没花。两者都该重试,但只有前者需要把代价摆到界面上,否则一个开着
|
|
73
|
+
* 无限重试的窗口可以安静地烧穿账单。
|
|
74
|
+
*/
|
|
75
|
+
costIncurred?: boolean;
|
|
76
|
+
}
|
|
77
|
+
export type FailureInput =
|
|
78
|
+
/**
|
|
79
|
+
* fetch 本身抛了:连接没建立,或者建立后断了。
|
|
80
|
+
*
|
|
81
|
+
* `sending`:异常是从发请求那一下(`fetch(...)` 本身)抛出来的,而不是读流、解码时。只有调用 fetch
|
|
82
|
+
* 的那一处知道这件事,见 `fromTransport` 里用到它的那一段。
|
|
83
|
+
*/
|
|
84
|
+
{
|
|
85
|
+
from: "transport";
|
|
86
|
+
error: unknown;
|
|
87
|
+
sending?: boolean;
|
|
88
|
+
}
|
|
89
|
+
/** 服务器答了,但状态码不是 2xx。 */
|
|
90
|
+
| {
|
|
91
|
+
from: "status";
|
|
92
|
+
status: number;
|
|
93
|
+
body?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 连接和状态码都正常,错误写在流里面。
|
|
97
|
+
*
|
|
98
|
+
* `streamed`:出错之前已经流到窗口里的正文(`spokenSoFar`)。服务商有时把拒绝当成回答写出来,再由中转
|
|
99
|
+
* 补一个泛泛的错误收尾,单看那个错误像是一次抖动——见 `refusalIn`。
|
|
100
|
+
*/
|
|
101
|
+
| {
|
|
102
|
+
from: "stream";
|
|
103
|
+
message?: string;
|
|
104
|
+
raw?: string;
|
|
105
|
+
spent?: boolean;
|
|
106
|
+
streamed?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 流正常结束,但一个字都没有——见 `fromEmpty`。
|
|
110
|
+
*
|
|
111
|
+
* `not-stream`:状态码 200、content-type 却明说是 JSON 或网页,正文根本不是 SSE(见 `retry.ts` 的
|
|
112
|
+
* `streamOrFailure`)。`output.truncated`:是不是撞了输出上限——撞上限的空不是故障。
|
|
113
|
+
*/
|
|
114
|
+
| {
|
|
115
|
+
from: "empty";
|
|
116
|
+
why: "no-content" | "no-frames" | "unparsable" | "not-stream";
|
|
117
|
+
body?: string;
|
|
118
|
+
output?: SpentOutput;
|
|
119
|
+
};
|
|
120
|
+
/** 空回答那一次,是不是撞了输出上限。 */
|
|
121
|
+
type SpentOutput = {
|
|
122
|
+
truncated: boolean;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* 写在错误正文里的终局理由。
|
|
126
|
+
*
|
|
127
|
+
* 中转很少给出规矩的状态码——一个欠费的账号可能收到 200 加一个流内 error 事件。能认出来的就认,
|
|
128
|
+
* 认不出来的落进 `upstream` 去重试,再由界面把「一直是同一个错误」说出来。所以这张表宁可短一些
|
|
129
|
+
* 也不要猜:错判成 `fatal` 会让一次本可以自愈的抖动直接终止,那比多重试几次糟得多。
|
|
130
|
+
*/
|
|
131
|
+
/** 模型拒绝作答时交给分类器的那句话;上面的内容策略那条认它,判成 `blocked`、不重试。 */
|
|
132
|
+
export declare const REFUSAL_MESSAGE = "\u6A21\u578B\u62D2\u7EDD\u56DE\u7B54\u8FD9\u6B21\u8BF7\u6C42";
|
|
133
|
+
/** 这次失败是不是上下文超长——循环据此决定要不要强制压缩一次再发。 */
|
|
134
|
+
export declare const isContextOverflow: (failure: Failure | undefined) => boolean;
|
|
135
|
+
/**
|
|
136
|
+
* 错误正文里那句真正说明问题的话。
|
|
137
|
+
*
|
|
138
|
+
* 每家中转把它放在不同的地方,所以按常见程度依次找。找不到就返回空,让调用方退回到状态码的固定
|
|
139
|
+
* 说法——那也比把整个 JSON 糊在界面上强。
|
|
140
|
+
*/
|
|
141
|
+
export declare function messageFromBody(body: string | undefined): string;
|
|
142
|
+
/**
|
|
143
|
+
* 服务器说要等多久,毫秒。
|
|
144
|
+
*
|
|
145
|
+
* 从 `Retry-After` 头和错误正文两处找,因为两边都有人用。正文那一路尤其要紧:我们面前这个中转
|
|
146
|
+
* 用 `{"reset_seconds":54}` 回答 503 而完全不发头,从前每一次等待都因此退回到猜的曲线上,几秒钟
|
|
147
|
+
* 就把整个重试预算花在一场它早就说明要持续一分钟的故障上。
|
|
148
|
+
*/
|
|
149
|
+
export declare function serverDelayMs(header: string | null | undefined, body?: string): number | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* `1m2s`、`6m0s`、`11.05s`、`20ms` 这种时长写法,毫秒。OpenAI 的 `x-ratelimit-reset-*` 头和
|
|
152
|
+
* 限流原话都这么写。整串都得是时长,认不出就是 `undefined`。
|
|
153
|
+
*/
|
|
154
|
+
export declare function durationMs(text: string): number | undefined;
|
|
155
|
+
export declare function classifyFailure(input: FailureInput): Failure;
|
|
156
|
+
/** 进行中的回复里已经显示出来的正文——人已经看到的那部分,不含思考。 */
|
|
157
|
+
export declare function spokenSoFar(content: readonly {
|
|
158
|
+
type: string;
|
|
159
|
+
text?: string;
|
|
160
|
+
}[]): string;
|
|
161
|
+
/** 值得再试一次吗。三层重试问的都是这一个问题。 */
|
|
162
|
+
export declare const worthRetrying: (failure: Failure) => boolean;
|
|
163
|
+
/**
|
|
164
|
+
* 一个已经判过的失败,被包成异常往上抛。
|
|
165
|
+
*
|
|
166
|
+
* 少了它就会判两遍,而第二遍是错的:一个 401 在状态码那一路被正确地判成 `fatal`,调用方把它抛成
|
|
167
|
+
* `new Error("HTTP 401: invalid key")`,到了流重试那一层就只剩一个字符串——从异常的角度看它既没有
|
|
168
|
+
* `cause.code` 也不认识自己,于是落进「认不出来的连接问题」,然后被无限重试。判断带着结论一起走,
|
|
169
|
+
* 上面每一层就都不必再猜。
|
|
170
|
+
*/
|
|
171
|
+
export declare class FailureError extends Error {
|
|
172
|
+
readonly failure: Failure;
|
|
173
|
+
constructor(failure: Failure);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 这个异常代表的失败是什么。
|
|
177
|
+
*
|
|
178
|
+
* 已经判过的照搬,没判过的当传输故障判——那是异常唯一可能的另一个来源。
|
|
179
|
+
*/
|
|
180
|
+
export declare function failureOf(error: unknown): Failure;
|
|
181
|
+
export {};
|