Skip to content
Merged
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
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ Documentation: [configuration reference](docs/config.md) ·
in a tool call for the client to run. Tool-call inspection can cut off an
answer whose tool call downloads and runs code, sends out environment
variables or credential files, reads private keys, or installs a startup item
or scheduled job, before the client receives it whole; hidden-character
detection, a content filter and an output limit complete the five
protections. All start
in observe mode (the output limit starts off) and change nothing until set to
enforce.
or scheduled job, before the client receives it whole.
- **Hidden instructions are removed.** Characters invisible on screen can carry
instructions that a model reads; the content filter can delete them from user
messages and tool results before a request leaves, or refuse a request that
tells the model to ignore its instructions. Outbound redaction, tool-call
inspection and the content filter all start in observe mode, which records
what they find and changes nothing.
- **Every request is traceable.** Each request is stored with the rule that
chose its upstream, every attempt, any format conversion, usage, cost and
where its price came from, time to first token and generation speed. A dry
Expand Down Expand Up @@ -133,7 +135,7 @@ a time, and cannot stop core, take the diagnostic bundle or change
| Crate | Role |
|---|---|
| `tw-dialect` | Conversion between the four API formats; usage parsing |
| `tw-guard` | The five protections: redaction, tool-call inspection, hidden characters, content filter, output limit |
| `tw-guard` | The three protections and their rules: outbound redaction, tool-call inspection, content filter |
| `tw-breaker` | Circuit-breaker state machine |
| `tw-bedrock` | Amazon Bedrock on the wire: SigV4 signing, eventstream, addresses, model catalog |
| `tw-types` | Messages for people: stable code, arguments, English sentence |
Expand Down
5 changes: 3 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ ThinkWatch Core 是 ThinkWatch 的网关引擎,由一组 Rust crate 及其构

- **一次接入,随时切换**。客户端只保留一个地址和一把密钥,更换上游或模型都在网关中完成,客户端无需改配置或重启。Anthropic Messages、OpenAI Chat Completions、OpenAI Responses 与 Gemini 四种格式双向转换,流式输出同样适用。
- **出站脱敏**。出站脱敏可在请求发出前把 API 密钥、私钥和连接串中的口令替换为占位符,并在回答回显时还原,中转站因此看不到真实的值。
- **拦截恶意工具调用**。中转站可以改写回答,塞入让客户端执行的工具调用。回答中的工具调用若是下载即执行、外发环境变量或凭据文件、读取私钥、写入开机启动项或定时任务,工具调用审查可以在客户端收到完整调用之前切断回答;另有隐藏字符检测、内容过滤和输出长度,共五项防护。出厂时除输出长度为关闭外均为观察档,切换到拦截档之前不改变任何请求。
- **切断恶意工具调用**。中转站可以改写回答,塞入让客户端执行的工具调用。回答中的工具调用若是下载即执行、外发环境变量或凭据文件、读取私钥、写入开机启动项或定时任务,工具调用审查可以在客户端收到完整调用之前切断回答。
- **清除隐藏指令**。屏幕上看不见的字符可以夹带模型会读取的指令,内容过滤可以在请求发出前把它们从用户消息和工具结果中删除,也可以拒绝要求模型忽略自身指令的请求。出站脱敏、工具调用审查和内容过滤出厂均为观察档,只记录检出的内容,不改变任何请求。
- **每个请求都可追溯**。每个请求连同决定其去向的规则、每次尝试、格式转换、用量、费用及价格来源、首 token 时间和生成速度一并保存。试算可以在不发出请求的情况下说明请求会被送往何处;已保存的请求可以对另一个上游重放,以便对比。
- **路由与故障转移**。规则可按模型、密钥、格式、请求大小、工具、图片、思考等条件匹配,把请求交给一个上游或策略组(按顺序、手动指定、轮流、最低延迟、最低价格)。响应的首字节到达客户端之前,失败的上游由下一个候选替换,并按其给出的失败原因暂停相应的时间。
- **多种上游**。服务商的 API 密钥、任意兼容接口、OpenRouter 等中转站、本地模型、Amazon Bedrock,以及 ChatGPT 和 Z.ai 账号。连通性检查和预热请求默认在本地应答,不产生费用。
Expand Down Expand Up @@ -76,7 +77,7 @@ twcore control-key --rotate # 更换密钥;用旧密钥建立的连接随
| crate | 职责 |
|---|---|
| `tw-dialect` | 四种接口格式之间的转换,用量解析 |
| `tw-guard` | 五项防护:出站脱敏、工具调用审查、隐藏字符、内容过滤、输出长度 |
| `tw-guard` | 三项防护及其规则:出站脱敏、工具调用审查、内容过滤 |
| `tw-breaker` | 熔断状态机 |
| `tw-bedrock` | Amazon Bedrock 的线上处理:SigV4 签名、eventstream、地址、模型目录 |
| `tw-types` | 给人看的消息:稳定的消息码、参数与英文句子 |
Expand Down
5 changes: 4 additions & 1 deletion crates/tw-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ serde_json = { workspace = true }
# tw-types,桌面端的接管 crate 也是),而契约这一层反过来依赖它没有问题 ——
# tw-types 自己零依赖。
tw-types = { workspace = true }
# 三项防护的规则视图和「测试…」的请求、结果在 tw-guard 里定义一份,两个产品共用;
# 这里重导出(`tw_api::guard`)。它也不碰 IO,只是多了匹配引擎的那几个依赖
tw-guard = { workspace = true }
ts-rs = { workspace = true, optional = true }

[features]
# 把契约导出成 TypeScript:`tw_api::ts::export_all(dir)`。**默认关** ——
# 只有桌面端生成前端类型时开,core 自己和企业版都不背 ts-rs。
ts = ["dep:ts-rs", "tw-types/ts"]
ts = ["dep:ts-rs", "tw-types/ts", "tw-guard/ts"]

[[example]]
name = "export_ts"
Expand Down
26 changes: 12 additions & 14 deletions crates/tw-api/msg-codes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ config.empty_models_only
config.failover_range
config.name_collision
config.no_clients
config.output_limit_range
config.rejected
config.rejected_at
config.remote_port_is_gateway
config.remote_port_zero
config.reserved_name
config.rotate.no_provider
config.rotate.read_back_differs
config.rule_codepoints_bad
config.rule_label_bad
config.rule_name_empty
config.rule_name_taken
config.rule_pattern_bad
Expand Down Expand Up @@ -223,13 +224,13 @@ gw.config.proxy_undefined
gw.config.proxy_unusable
gw.config.security_rules passthrough
gw.content.refused
gw.content.refused_invisible_message
gw.content.refused_invisible_tool_result
gw.convert.failed
gw.convert.tool_unsendable
gw.convert.tools_unsendable
gw.count_tokens.bedrock_upstream
gw.files.unsupported
gw.hidden_text.refused_message
gw.hidden_text.refused_tool_result
gw.internal
gw.listen.addr_unavailable
gw.listen.bind_failed
Expand Down Expand Up @@ -258,8 +259,6 @@ gw.oauth.rotation_no_manager
gw.oauth.rotation_queue_full
gw.oauth.status
gw.oauth.unreachable
gw.output_limit.cut
gw.output_limit.withheld
gw.probe.aws_token_expired
gw.probe.bedrock_list_denied
gw.probe.connect
Expand All @@ -275,8 +274,9 @@ gw.route.protocol_mismatch
gw.route.rule_failed
gw.route.selected_upstream_missing
gw.route.upstream_missing
gw.toolcall.blocked
gw.toolcall.cut
gw.toolcall.connection_cut
gw.toolcall.response_cut
gw.toolcall.response_withheld
gw.upstream.aws_profile_expired
gw.upstream.aws_token_expired
gw.upstream.bedrock_refused
Expand All @@ -297,7 +297,6 @@ gw.ws.bad_url
gw.ws.connect_failed
gw.ws.proxy_unsupported
gw.ws.send_failed
gw.ws.toolcall_cut
gw.ws.upstream_broke
l1.config.bad_url
l1.config.no_host
Expand Down Expand Up @@ -366,17 +365,16 @@ pricing.sheet.empty_model
pricing.sheet.empty_name
pricing.sheet.half_long_context
pricing.sheet.padded_name
security.bad_codepoints
security.bad_content_pattern
security.bad_label
security.bad_pattern
security.guard_unknown
security.limit_range
security.content_action_unknown
security.no_action_of_its_own
security.no_custom_rules
security.no_limit
security.nothing_to_test
security.pattern_empty
security.rule_name_empty
security.unknown_action
security.unknown_content_action
security.unknown_guard
security.unknown_rule
t.auth test
t.broke test
Expand Down
2 changes: 0 additions & 2 deletions crates/tw-api/src/ep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ endpoints! {
SetSecurityMode: PUT "/security/{guard}/mode" [guard], api::ModeSave => api::ConfigWritten;
ToggleBuiltinRule: PUT "/security/{guard}/builtin/{id}" [guard, id], api::RuleToggle => api::ConfigWritten;
SetBuiltinRuleAction: PUT "/security/{guard}/builtin/{id}/action" [guard, id], api::ActionSave => api::ConfigWritten;
/// 只有 `output_limit` 有上限
SetSecurityLimit: PUT "/security/{guard}/limit" [guard], api::LimitSave => api::ConfigWritten;
CreateCustomRule: POST "/security/{guard}/custom" [guard], api::CustomRuleSave => api::ConfigWritten;
UpdateCustomRule: PUT "/security/{guard}/custom/{name}" [guard, name], api::CustomRuleSave => api::ConfigWritten;
DeleteCustomRule: DELETE "/security/{guard}/custom/{name}" [guard, name], api::BaseVersion => api::ConfigWritten;
Expand Down
Loading
Loading