llm.azure_foundry.js
2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
* Azure AI Foundry OpenAI-compatible provider.
*
* Azure Foundry endpoints used by this repo expose an OpenAI-compatible
* `/chat/completions` API when `LLM_BASE_URL` points at the resource's
* `/openai/v1` base URL. Request/stream parsing is therefore identical to the
* OpenAI provider. Azure resource keys use the `api-key` header, and there is
* no safe global default host for Azure deployments, so baseUrl must be
* injected by the key provider.
*
* Filename `llm.azure_foundry.js` registers provider key `azure_foundry`.
*/
import { LLMError } from '../errors.js'
import OpenAIProvider from './llm.openai.js'
export default class AzureFoundryProvider extends OpenAIProvider {
/**
* Azure 将内容安全拦截作为 HTTP 400 返回。对剧情运行时,这通常是供应商对完整 prompt
* 或生成方向的拒绝,不等价于玩家请求参数错误;允许路由层切到下一个供应商。
* @protected
* @param {Response} res
* @param {unknown} body
* @param {string} reason
*/
httpErrorInfo(res, body, reason) {
if (isContentFilterError(body)) {
return {
reason: 'contentFilter',
retryable: true,
message: '[azure_foundry] content filtered',
provider_request_id: res.headers.get('x-request-id'),
}
}
return super.httpErrorInfo(res, body, reason)
}
/**
* @protected
* @param {import('../index.js').KeyLease} lease
* @returns {Record<string, string>}
*/
chatCompletionsHeaders(lease) {
return {
'content-type': 'application/json',
'api-key': lease.api_key,
}
}
/**
* @protected
* @param {string|undefined} model
* @returns {string}
*/
maxTokensParamName(model) {
return isGpt5Model(model) ? 'max_completion_tokens' : 'max_tokens'
}
/**
* Azure Foundry OpenAI v1 rejects the non-standard `thinking` parameter.
* @protected
* @returns {boolean}
*/
supportsThinkingParam() {
return false
}
/** @returns {string} */
defaultBaseUrl() {
throw new LLMError({
provider: this.provider,
reason: 'badRequest',
retryable: false,
message: 'azure_foundry requires LLM_BASE_URL, for example https://<resource>.services.ai.azure.com/openai/v1',
})
}
}
/**
* @param {string|undefined} model
* @returns {boolean}
*/
function isGpt5Model(model) {
return /^gpt-5(?:[.-]|$)/i.test(String(model || ''))
}
function isContentFilterError(body) {
if (!body || Object.getPrototypeOf(body) !== Object.prototype) return false
const error = body.error
if (!error || Object.getPrototypeOf(error) !== Object.prototype) return false
return error.code === 'content_filter'
}