mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-19 09:18:38 +00:00
feat: gate Claude inference_geo passthrough behind channel setting and add field docs
This commit is contained in:
@@ -29,6 +29,7 @@ type ChannelOtherSettings struct {
|
|||||||
OpenRouterEnterprise *bool `json:"openrouter_enterprise,omitempty"`
|
OpenRouterEnterprise *bool `json:"openrouter_enterprise,omitempty"`
|
||||||
ClaudeBetaQuery bool `json:"claude_beta_query,omitempty"` // Claude 渠道是否强制追加 ?beta=true
|
ClaudeBetaQuery bool `json:"claude_beta_query,omitempty"` // Claude 渠道是否强制追加 ?beta=true
|
||||||
AllowServiceTier bool `json:"allow_service_tier,omitempty"` // 是否允许 service_tier 透传(默认过滤以避免额外计费)
|
AllowServiceTier bool `json:"allow_service_tier,omitempty"` // 是否允许 service_tier 透传(默认过滤以避免额外计费)
|
||||||
|
AllowInferenceGeo bool `json:"allow_inference_geo,omitempty"` // 是否允许 inference_geo 透传(仅 Claude,默认过滤以满足数据驻留合规)
|
||||||
DisableStore bool `json:"disable_store,omitempty"` // 是否禁用 store 透传(默认允许透传,禁用后可能导致 Codex 无法使用)
|
DisableStore bool `json:"disable_store,omitempty"` // 是否禁用 store 透传(默认允许透传,禁用后可能导致 Codex 无法使用)
|
||||||
AllowSafetyIdentifier bool `json:"allow_safety_identifier,omitempty"` // 是否允许 safety_identifier 透传(默认过滤以保护用户隐私)
|
AllowSafetyIdentifier bool `json:"allow_safety_identifier,omitempty"` // 是否允许 safety_identifier 透传(默认过滤以保护用户隐私)
|
||||||
AllowIncludeObfuscation bool `json:"allow_include_obfuscation,omitempty"` // 是否允许 stream_options.include_obfuscation 透传(默认过滤以避免关闭流混淆保护)
|
AllowIncludeObfuscation bool `json:"allow_include_obfuscation,omitempty"` // 是否允许 stream_options.include_obfuscation 透传(默认过滤以避免关闭流混淆保护)
|
||||||
|
|||||||
@@ -194,8 +194,9 @@ type ClaudeRequest struct {
|
|||||||
Prompt string `json:"prompt,omitempty"`
|
Prompt string `json:"prompt,omitempty"`
|
||||||
System any `json:"system,omitempty"`
|
System any `json:"system,omitempty"`
|
||||||
Messages []ClaudeMessage `json:"messages,omitempty"`
|
Messages []ClaudeMessage `json:"messages,omitempty"`
|
||||||
// https://platform.claude.com/docs/en/build-with-claude/data-residency#inference-geo
|
// InferenceGeo controls Claude data residency region.
|
||||||
// InferenceGeo string `json:"inference_geo,omitempty"`
|
// This field is filtered by default and can be enabled via channel setting allow_inference_geo.
|
||||||
|
InferenceGeo string `json:"inference_geo,omitempty"`
|
||||||
MaxTokens uint `json:"max_tokens,omitempty"`
|
MaxTokens uint `json:"max_tokens,omitempty"`
|
||||||
MaxTokensToSample uint `json:"max_tokens_to_sample,omitempty"`
|
MaxTokensToSample uint `json:"max_tokens_to_sample,omitempty"`
|
||||||
StopSequences []string `json:"stop_sequences,omitempty"`
|
StopSequences []string `json:"stop_sequences,omitempty"`
|
||||||
@@ -212,7 +213,8 @@ type ClaudeRequest struct {
|
|||||||
Thinking *Thinking `json:"thinking,omitempty"`
|
Thinking *Thinking `json:"thinking,omitempty"`
|
||||||
McpServers json.RawMessage `json:"mcp_servers,omitempty"`
|
McpServers json.RawMessage `json:"mcp_servers,omitempty"`
|
||||||
Metadata json.RawMessage `json:"metadata,omitempty"`
|
Metadata json.RawMessage `json:"metadata,omitempty"`
|
||||||
// 服务层级字段,用于指定 API 服务等级。允许透传可能导致实际计费高于预期,默认应过滤
|
// ServiceTier specifies upstream service level and may affect billing.
|
||||||
|
// This field is filtered by default and can be enabled via channel setting allow_service_tier.
|
||||||
ServiceTier string `json:"service_tier,omitempty"`
|
ServiceTier string `json:"service_tier,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,18 +56,20 @@ type GeneralOpenAIRequest struct {
|
|||||||
ToolChoice any `json:"tool_choice,omitempty"`
|
ToolChoice any `json:"tool_choice,omitempty"`
|
||||||
FunctionCall json.RawMessage `json:"function_call,omitempty"`
|
FunctionCall json.RawMessage `json:"function_call,omitempty"`
|
||||||
User string `json:"user,omitempty"`
|
User string `json:"user,omitempty"`
|
||||||
ServiceTier string `json:"service_tier,omitempty"`
|
// ServiceTier specifies upstream service level and may affect billing.
|
||||||
LogProbs bool `json:"logprobs,omitempty"`
|
// This field is filtered by default and can be enabled via channel setting allow_service_tier.
|
||||||
TopLogProbs int `json:"top_logprobs,omitempty"`
|
ServiceTier string `json:"service_tier,omitempty"`
|
||||||
Dimensions int `json:"dimensions,omitempty"`
|
LogProbs bool `json:"logprobs,omitempty"`
|
||||||
Modalities json.RawMessage `json:"modalities,omitempty"`
|
TopLogProbs int `json:"top_logprobs,omitempty"`
|
||||||
Audio json.RawMessage `json:"audio,omitempty"`
|
Dimensions int `json:"dimensions,omitempty"`
|
||||||
|
Modalities json.RawMessage `json:"modalities,omitempty"`
|
||||||
|
Audio json.RawMessage `json:"audio,omitempty"`
|
||||||
// 安全标识符,用于帮助 OpenAI 检测可能违反使用政策的应用程序用户
|
// 安全标识符,用于帮助 OpenAI 检测可能违反使用政策的应用程序用户
|
||||||
// 注意:此字段会向 OpenAI 发送用户标识信息,默认过滤以保护用户隐私
|
// 注意:此字段会向 OpenAI 发送用户标识信息,默认过滤,可通过 allow_safety_identifier 开启
|
||||||
SafetyIdentifier string `json:"safety_identifier,omitempty"`
|
SafetyIdentifier string `json:"safety_identifier,omitempty"`
|
||||||
// Whether or not to store the output of this chat completion request for use in our model distillation or evals products.
|
// Whether or not to store the output of this chat completion request for use in our model distillation or evals products.
|
||||||
// 是否存储此次请求数据供 OpenAI 用于评估和优化产品
|
// 是否存储此次请求数据供 OpenAI 用于评估和优化产品
|
||||||
// 注意:默认过滤此字段以保护用户隐私,但过滤后可能导致 Codex 无法正常使用
|
// 注意:默认允许透传,可通过 disable_store 禁用;禁用后可能导致 Codex 无法正常使用
|
||||||
Store json.RawMessage `json:"store,omitempty"`
|
Store json.RawMessage `json:"store,omitempty"`
|
||||||
// Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the user field
|
// Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the user field
|
||||||
PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
||||||
@@ -263,7 +265,8 @@ type FunctionRequest struct {
|
|||||||
|
|
||||||
type StreamOptions struct {
|
type StreamOptions struct {
|
||||||
IncludeUsage bool `json:"include_usage,omitempty"`
|
IncludeUsage bool `json:"include_usage,omitempty"`
|
||||||
// for /v1/responses
|
// IncludeObfuscation is only for /v1/responses stream payload.
|
||||||
|
// This field is filtered by default and can be enabled via channel setting allow_include_obfuscation.
|
||||||
IncludeObfuscation bool `json:"include_obfuscation,omitempty"`
|
IncludeObfuscation bool `json:"include_obfuscation,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,23 +820,28 @@ type OpenAIResponsesRequest struct {
|
|||||||
ParallelToolCalls json.RawMessage `json:"parallel_tool_calls,omitempty"`
|
ParallelToolCalls json.RawMessage `json:"parallel_tool_calls,omitempty"`
|
||||||
PreviousResponseID string `json:"previous_response_id,omitempty"`
|
PreviousResponseID string `json:"previous_response_id,omitempty"`
|
||||||
Reasoning *Reasoning `json:"reasoning,omitempty"`
|
Reasoning *Reasoning `json:"reasoning,omitempty"`
|
||||||
// 服务层级字段,用于指定 API 服务等级。允许透传可能导致实际计费高于预期,默认应过滤
|
// ServiceTier specifies upstream service level and may affect billing.
|
||||||
ServiceTier string `json:"service_tier,omitempty"`
|
// This field is filtered by default and can be enabled via channel setting allow_service_tier.
|
||||||
|
ServiceTier string `json:"service_tier,omitempty"`
|
||||||
|
// Store controls whether upstream may store request/response data.
|
||||||
|
// This field is allowed by default and can be disabled via channel setting disable_store.
|
||||||
Store json.RawMessage `json:"store,omitempty"`
|
Store json.RawMessage `json:"store,omitempty"`
|
||||||
PromptCacheKey json.RawMessage `json:"prompt_cache_key,omitempty"`
|
PromptCacheKey json.RawMessage `json:"prompt_cache_key,omitempty"`
|
||||||
PromptCacheRetention json.RawMessage `json:"prompt_cache_retention,omitempty"`
|
PromptCacheRetention json.RawMessage `json:"prompt_cache_retention,omitempty"`
|
||||||
SafetyIdentifier string `json:"safety_identifier,omitempty"`
|
// SafetyIdentifier carries client identity for policy abuse detection.
|
||||||
Stream bool `json:"stream,omitempty"`
|
// This field is filtered by default and can be enabled via channel setting allow_safety_identifier.
|
||||||
StreamOptions *StreamOptions `json:"stream_options,omitempty"`
|
SafetyIdentifier string `json:"safety_identifier,omitempty"`
|
||||||
Temperature *float64 `json:"temperature,omitempty"`
|
Stream bool `json:"stream,omitempty"`
|
||||||
Text json.RawMessage `json:"text,omitempty"`
|
StreamOptions *StreamOptions `json:"stream_options,omitempty"`
|
||||||
ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
|
Temperature *float64 `json:"temperature,omitempty"`
|
||||||
Tools json.RawMessage `json:"tools,omitempty"` // 需要处理的参数很少,MCP 参数太多不确定,所以用 map
|
Text json.RawMessage `json:"text,omitempty"`
|
||||||
TopP *float64 `json:"top_p,omitempty"`
|
ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
|
||||||
Truncation string `json:"truncation,omitempty"`
|
Tools json.RawMessage `json:"tools,omitempty"` // 需要处理的参数很少,MCP 参数太多不确定,所以用 map
|
||||||
User string `json:"user,omitempty"`
|
TopP *float64 `json:"top_p,omitempty"`
|
||||||
MaxToolCalls uint `json:"max_tool_calls,omitempty"`
|
Truncation string `json:"truncation,omitempty"`
|
||||||
Prompt json.RawMessage `json:"prompt,omitempty"`
|
User string `json:"user,omitempty"`
|
||||||
|
MaxToolCalls uint `json:"max_tool_calls,omitempty"`
|
||||||
|
Prompt json.RawMessage `json:"prompt,omitempty"`
|
||||||
// qwen
|
// qwen
|
||||||
EnableThinking json.RawMessage `json:"enable_thinking,omitempty"`
|
EnableThinking json.RawMessage `json:"enable_thinking,omitempty"`
|
||||||
// perplexity
|
// perplexity
|
||||||
|
|||||||
@@ -812,6 +812,39 @@ func TestRemoveDisabledFieldsSkipWhenGlobalPassThroughEnabled(t *testing.T) {
|
|||||||
assertJSONEqual(t, input, string(out))
|
assertJSONEqual(t, input, string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoveDisabledFieldsDefaultFiltering(t *testing.T) {
|
||||||
|
input := `{
|
||||||
|
"service_tier":"flex",
|
||||||
|
"inference_geo":"eu",
|
||||||
|
"safety_identifier":"user-123",
|
||||||
|
"store":true,
|
||||||
|
"stream_options":{"include_obfuscation":false}
|
||||||
|
}`
|
||||||
|
settings := dto.ChannelOtherSettings{}
|
||||||
|
|
||||||
|
out, err := RemoveDisabledFields([]byte(input), settings, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RemoveDisabledFields returned error: %v", err)
|
||||||
|
}
|
||||||
|
assertJSONEqual(t, `{"store":true}`, string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveDisabledFieldsAllowInferenceGeo(t *testing.T) {
|
||||||
|
input := `{
|
||||||
|
"inference_geo":"eu",
|
||||||
|
"store":true
|
||||||
|
}`
|
||||||
|
settings := dto.ChannelOtherSettings{
|
||||||
|
AllowInferenceGeo: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := RemoveDisabledFields([]byte(input), settings, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RemoveDisabledFields returned error: %v", err)
|
||||||
|
}
|
||||||
|
assertJSONEqual(t, `{"inference_geo":"eu","store":true}`, string(out))
|
||||||
|
}
|
||||||
|
|
||||||
func assertJSONEqual(t *testing.T, want, got string) {
|
func assertJSONEqual(t *testing.T, want, got string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|||||||
@@ -700,6 +700,7 @@ func FailTaskInfo(reason string) *TaskInfo {
|
|||||||
|
|
||||||
// RemoveDisabledFields 从请求 JSON 数据中移除渠道设置中禁用的字段
|
// RemoveDisabledFields 从请求 JSON 数据中移除渠道设置中禁用的字段
|
||||||
// service_tier: 服务层级字段,可能导致额外计费(OpenAI、Claude、Responses API 支持)
|
// service_tier: 服务层级字段,可能导致额外计费(OpenAI、Claude、Responses API 支持)
|
||||||
|
// inference_geo: Claude 数据驻留推理区域字段(仅 Claude 支持,默认过滤)
|
||||||
// store: 数据存储授权字段,涉及用户隐私(仅 OpenAI、Responses API 支持,默认允许透传,禁用后可能导致 Codex 无法使用)
|
// store: 数据存储授权字段,涉及用户隐私(仅 OpenAI、Responses API 支持,默认允许透传,禁用后可能导致 Codex 无法使用)
|
||||||
// safety_identifier: 安全标识符,用于向 OpenAI 报告违规用户(仅 OpenAI 支持,涉及用户隐私)
|
// safety_identifier: 安全标识符,用于向 OpenAI 报告违规用户(仅 OpenAI 支持,涉及用户隐私)
|
||||||
// stream_options.include_obfuscation: 响应流混淆控制字段(仅 OpenAI Responses API 支持)
|
// stream_options.include_obfuscation: 响应流混淆控制字段(仅 OpenAI Responses API 支持)
|
||||||
@@ -721,6 +722,13 @@ func RemoveDisabledFields(jsonData []byte, channelOtherSettings dto.ChannelOther
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 默认移除 inference_geo,除非明确允许(避免在未授权情况下透传数据驻留区域)
|
||||||
|
if !channelOtherSettings.AllowInferenceGeo {
|
||||||
|
if _, exists := data["inference_geo"]; exists {
|
||||||
|
delete(data, "inference_geo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 默认允许 store 透传,除非明确禁用(禁用可能影响 Codex 使用)
|
// 默认允许 store 透传,除非明确禁用(禁用可能影响 Codex 使用)
|
||||||
if channelOtherSettings.DisableStore {
|
if channelOtherSettings.DisableStore {
|
||||||
if _, exists := data["store"]; exists {
|
if _, exists := data["store"]; exists {
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ const EditChannelModal = (props) => {
|
|||||||
disable_store: false, // false = 允许透传(默认开启)
|
disable_store: false, // false = 允许透传(默认开启)
|
||||||
allow_safety_identifier: false,
|
allow_safety_identifier: false,
|
||||||
allow_include_obfuscation: false,
|
allow_include_obfuscation: false,
|
||||||
|
allow_inference_geo: false,
|
||||||
claude_beta_query: false,
|
claude_beta_query: false,
|
||||||
};
|
};
|
||||||
const [batch, setBatch] = useState(false);
|
const [batch, setBatch] = useState(false);
|
||||||
@@ -637,6 +638,8 @@ const EditChannelModal = (props) => {
|
|||||||
parsedSettings.allow_safety_identifier || false;
|
parsedSettings.allow_safety_identifier || false;
|
||||||
data.allow_include_obfuscation =
|
data.allow_include_obfuscation =
|
||||||
parsedSettings.allow_include_obfuscation || false;
|
parsedSettings.allow_include_obfuscation || false;
|
||||||
|
data.allow_inference_geo =
|
||||||
|
parsedSettings.allow_inference_geo || false;
|
||||||
data.claude_beta_query = parsedSettings.claude_beta_query || false;
|
data.claude_beta_query = parsedSettings.claude_beta_query || false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('解析其他设置失败:', error);
|
console.error('解析其他设置失败:', error);
|
||||||
@@ -649,6 +652,7 @@ const EditChannelModal = (props) => {
|
|||||||
data.disable_store = false;
|
data.disable_store = false;
|
||||||
data.allow_safety_identifier = false;
|
data.allow_safety_identifier = false;
|
||||||
data.allow_include_obfuscation = false;
|
data.allow_include_obfuscation = false;
|
||||||
|
data.allow_inference_geo = false;
|
||||||
data.claude_beta_query = false;
|
data.claude_beta_query = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -660,6 +664,7 @@ const EditChannelModal = (props) => {
|
|||||||
data.disable_store = false;
|
data.disable_store = false;
|
||||||
data.allow_safety_identifier = false;
|
data.allow_safety_identifier = false;
|
||||||
data.allow_include_obfuscation = false;
|
data.allow_include_obfuscation = false;
|
||||||
|
data.allow_inference_geo = false;
|
||||||
data.claude_beta_query = false;
|
data.claude_beta_query = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1406,6 +1411,7 @@ const EditChannelModal = (props) => {
|
|||||||
localInputs.allow_include_obfuscation === true;
|
localInputs.allow_include_obfuscation === true;
|
||||||
}
|
}
|
||||||
if (localInputs.type === 14) {
|
if (localInputs.type === 14) {
|
||||||
|
settings.allow_inference_geo = localInputs.allow_inference_geo === true;
|
||||||
settings.claude_beta_query = localInputs.claude_beta_query === true;
|
settings.claude_beta_query = localInputs.claude_beta_query === true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1429,6 +1435,7 @@ const EditChannelModal = (props) => {
|
|||||||
delete localInputs.disable_store;
|
delete localInputs.disable_store;
|
||||||
delete localInputs.allow_safety_identifier;
|
delete localInputs.allow_safety_identifier;
|
||||||
delete localInputs.allow_include_obfuscation;
|
delete localInputs.allow_include_obfuscation;
|
||||||
|
delete localInputs.allow_inference_geo;
|
||||||
delete localInputs.claude_beta_query;
|
delete localInputs.claude_beta_query;
|
||||||
|
|
||||||
let res;
|
let res;
|
||||||
@@ -3322,6 +3329,22 @@ const EditChannelModal = (props) => {
|
|||||||
'service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用',
|
'service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Form.Switch
|
||||||
|
field='allow_inference_geo'
|
||||||
|
label={t('允许 inference_geo 透传')}
|
||||||
|
checkedText={t('开')}
|
||||||
|
uncheckedText={t('关')}
|
||||||
|
onChange={(value) =>
|
||||||
|
handleChannelOtherSettingsChange(
|
||||||
|
'allow_inference_geo',
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
extraText={t(
|
||||||
|
'inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user