Compare commits

...

7 Commits

Author SHA1 Message Date
github-actions[bot]
0f5321b0ef chore: sync VERSION file with release v1.1.260 [skip ci] 2026-01-21 02:19:34 +00:00
shaw
c7d7bf47d6 fix: 更新claude账号oauth链接生成规则 2026-01-21 10:06:24 +08:00
Wesley Liddick
ebc30b6026 Merge pull request #906 from 0xRichardH/fix-bedrock-sse-stream-event [skip ci]
Fix bedrock sse stream event
2026-01-21 09:38:19 +08:00
Wesley Liddick
d5a7af2d7d Merge pull request #903 from RedwindA/main [skip ci]
feat(droid): add prompt_cache_retention and safety_identifier to fiel…
2026-01-21 09:37:19 +08:00
Richard Hao
81a3e26e27 fix: correct Bedrock SSE stream event format to match Claude API spec
- message_start: nest fields inside 'message' object with type: 'message'
- content_block_delta: add type field to data
- message_delta: add type field to data
- message_stop: remove usage field, just return type
- Extract usage from message_delta instead of message_stop
2026-01-18 11:38:38 +08:00
Richard Hao
64db4a270d fix: handle bedrock content block start/stop events 2026-01-18 10:58:11 +08:00
RedwindA
ca027ecb90 feat(droid): add prompt_cache_retention and safety_identifier to fieldsToRemove 2026-01-16 04:22:05 +08:00
4 changed files with 44 additions and 15 deletions

View File

@@ -1 +1 @@
1.1.259 1.1.260

View File

@@ -274,7 +274,9 @@ const handleResponses = async (req, res) => {
'text_formatting', 'text_formatting',
'truncation', 'truncation',
'text', 'text',
'service_tier' 'service_tier',
'prompt_cache_retention',
'safety_identifier'
] ]
fieldsToRemove.forEach((field) => { fieldsToRemove.forEach((field) => {
delete req.body[field] delete req.body[field]

View File

@@ -343,8 +343,8 @@ class BedrockRelayService {
res.write(`event: ${claudeEvent.type}\n`) res.write(`event: ${claudeEvent.type}\n`)
res.write(`data: ${JSON.stringify(claudeEvent.data)}\n\n`) res.write(`data: ${JSON.stringify(claudeEvent.data)}\n\n`)
// 提取使用统计 // 提取使用统计 (usage is reported in message_delta per Claude API spec)
if (claudeEvent.type === 'message_stop' && claudeEvent.data.usage) { if (claudeEvent.type === 'message_delta' && claudeEvent.data.usage) {
totalUsage = claudeEvent.data.usage totalUsage = claudeEvent.data.usage
} }
@@ -576,8 +576,10 @@ class BedrockRelayService {
return { return {
type: 'message_start', type: 'message_start',
data: { data: {
type: 'message', type: 'message_start',
message: {
id: `msg_${Date.now()}_bedrock`, id: `msg_${Date.now()}_bedrock`,
type: 'message',
role: 'assistant', role: 'assistant',
content: [], content: [],
model: this.defaultModel, model: this.defaultModel,
@@ -587,21 +589,45 @@ class BedrockRelayService {
} }
} }
} }
}
if (bedrockChunk.type === 'content_block_start') {
return {
type: 'content_block_start',
data: {
type: 'content_block_start',
index: bedrockChunk.index || 0,
content_block: bedrockChunk.content_block || { type: 'text', text: '' }
}
}
}
if (bedrockChunk.type === 'content_block_delta') { if (bedrockChunk.type === 'content_block_delta') {
return { return {
type: 'content_block_delta', type: 'content_block_delta',
data: { data: {
type: 'content_block_delta',
index: bedrockChunk.index || 0, index: bedrockChunk.index || 0,
delta: bedrockChunk.delta || {} delta: bedrockChunk.delta || {}
} }
} }
} }
if (bedrockChunk.type === 'content_block_stop') {
return {
type: 'content_block_stop',
data: {
type: 'content_block_stop',
index: bedrockChunk.index || 0
}
}
}
if (bedrockChunk.type === 'message_delta') { if (bedrockChunk.type === 'message_delta') {
return { return {
type: 'message_delta', type: 'message_delta',
data: { data: {
type: 'message_delta',
delta: bedrockChunk.delta || {}, delta: bedrockChunk.delta || {},
usage: bedrockChunk.usage || {} usage: bedrockChunk.usage || {}
} }
@@ -612,7 +638,7 @@ class BedrockRelayService {
return { return {
type: 'message_stop', type: 'message_stop',
data: { data: {
usage: bedrockChunk.usage || {} type: 'message_stop'
} }
} }
} }

View File

@@ -13,8 +13,8 @@ const OAUTH_CONFIG = {
AUTHORIZE_URL: 'https://claude.ai/oauth/authorize', AUTHORIZE_URL: 'https://claude.ai/oauth/authorize',
TOKEN_URL: 'https://console.anthropic.com/v1/oauth/token', TOKEN_URL: 'https://console.anthropic.com/v1/oauth/token',
CLIENT_ID: '9d1c250a-e61b-44d9-88ed-5944d1962f5e', CLIENT_ID: '9d1c250a-e61b-44d9-88ed-5944d1962f5e',
REDIRECT_URI: 'https://console.anthropic.com/oauth/code/callback', REDIRECT_URI: 'https://platform.claude.com/oauth/code/callback',
SCOPES: 'org:create_api_key user:profile user:inference', SCOPES: 'org:create_api_key user:profile user:inference user:sessions:claude_code',
SCOPES_SETUP: 'user:inference' // Setup Token 只需要推理权限 SCOPES_SETUP: 'user:inference' // Setup Token 只需要推理权限
} }
@@ -35,6 +35,7 @@ function generateState() {
/** /**
* 生成随机的 code verifierPKCE * 生成随机的 code verifierPKCE
* 符合 RFC 7636 标准32字节随机数 → base64url编码 → 43字符
* @returns {string} base64url 编码的随机字符串 * @returns {string} base64url 编码的随机字符串
*/ */
function generateCodeVerifier() { function generateCodeVerifier() {