fix(openai): Anthropic history 표현을 정규화한다
This commit is contained in:
parent
1f1be982c9
commit
949db240a9
2 changed files with 70 additions and 16 deletions
|
|
@ -306,7 +306,7 @@ func TestLogicalRequestEndpointContinuationLineage(t *testing.T) {
|
|||
// Anthropic continuation test
|
||||
anthropicInitialRaw := []byte(`{
|
||||
"model": "preset-model",
|
||||
"system": [{"type": "text", "text": "sys"}],
|
||||
"system": "sys",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"tools": [{"name": "artifact", "input_schema": {"type": "object", "maximum": 9007199254740992}}]
|
||||
}`)
|
||||
|
|
@ -317,9 +317,9 @@ func TestLogicalRequestEndpointContinuationLineage(t *testing.T) {
|
|||
|
||||
anthropicContinuationRaw := []byte(`{
|
||||
"model": "preset-model",
|
||||
"system": [{"type": "text", "text": "sys"}],
|
||||
"system": [{"type": "text", "text": "sys", "cache_control":{"type":"ephemeral"}}],
|
||||
"messages": [
|
||||
{"role": "user", "content": "hello"},
|
||||
{"role": "user", "content": [{"type":"text","text":"hello","cache_control":{"type":"ephemeral"}}]},
|
||||
{"role": "assistant", "content": [{"type": "tool_use", "id": "tu_1", "name": "artifact", "input": {}}]},
|
||||
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "tu_1", "content": "ok"}]}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -62,7 +62,71 @@ func newAnthropicRequestLineage(raw json.RawMessage) (logicalRequestLineage, err
|
|||
if _, err := validateAnthropicMessages(rawMessages); err != nil {
|
||||
return logicalRequestLineage{}, err
|
||||
}
|
||||
return newLogicalRequestLineageFromRawFields(fields, logicalRequestEndpointAnthropic, []string{"model", "system", "messages"})
|
||||
historyDigest, err := fingerprintAnthropicHistory(fields["model"], fields["system"], rawMessages)
|
||||
if err != nil {
|
||||
return logicalRequestLineage{}, err
|
||||
}
|
||||
toolsetDigest, err := fingerprintCanonicalJSON(logicalRequestEndpointAnthropic, fields["tools"])
|
||||
if err != nil {
|
||||
return logicalRequestLineage{}, err
|
||||
}
|
||||
return logicalRequestLineage{Endpoint: logicalRequestEndpointAnthropic, HistoryDigest: historyDigest, ToolsetDigest: toolsetDigest}, nil
|
||||
}
|
||||
|
||||
func fingerprintAnthropicHistory(model, system, messages json.RawMessage) (string, error) {
|
||||
normalizedSystem, err := normalizeAnthropicHistoryContent(system)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
normalizedMessages, err := normalizeAnthropicHistoryMessages(messages)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fingerprintCanonicalJSON(logicalRequestEndpointAnthropic, map[string]json.RawMessage{
|
||||
"model": model, "system": normalizedSystem, "messages": normalizedMessages,
|
||||
})
|
||||
}
|
||||
|
||||
func normalizeAnthropicHistoryMessages(raw json.RawMessage) (json.RawMessage, error) {
|
||||
var messages []map[string]any
|
||||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&messages); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, message := range messages {
|
||||
message["content"] = normalizeAnthropicHistoryContentValue(message["content"])
|
||||
}
|
||||
return json.Marshal(messages)
|
||||
}
|
||||
|
||||
func normalizeAnthropicHistoryContent(raw json.RawMessage) (json.RawMessage, error) {
|
||||
if len(raw) == 0 {
|
||||
return raw, nil
|
||||
}
|
||||
var value any
|
||||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(normalizeAnthropicHistoryContentValue(value))
|
||||
}
|
||||
|
||||
func normalizeAnthropicHistoryContentValue(value any) any {
|
||||
if text, ok := value.(string); ok {
|
||||
return []any{map[string]any{"type": "text", "text": text}}
|
||||
}
|
||||
blocks, ok := value.([]any)
|
||||
if !ok {
|
||||
return value
|
||||
}
|
||||
for _, rawBlock := range blocks {
|
||||
if block, ok := rawBlock.(map[string]any); ok {
|
||||
delete(block, "cache_control")
|
||||
}
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
type chatMessageValidation struct {
|
||||
|
|
@ -532,12 +596,7 @@ func newAnthropicContinuationLineage(raw json.RawMessage) (logicalRequestContinu
|
|||
return logicalRequestContinuationLineage{}, fmt.Errorf("marshal prefix messages: %w", err)
|
||||
}
|
||||
|
||||
prefixHistory := map[string]json.RawMessage{
|
||||
"model": fields["model"],
|
||||
"system": fields["system"],
|
||||
"messages": prefixMessagesRaw,
|
||||
}
|
||||
prefixHistoryDigest, err := fingerprintCanonicalJSON(logicalRequestEndpointAnthropic, prefixHistory)
|
||||
prefixHistoryDigest, err := fingerprintAnthropicHistory(fields["model"], fields["system"], prefixMessagesRaw)
|
||||
if err != nil {
|
||||
return logicalRequestContinuationLineage{}, err
|
||||
}
|
||||
|
|
@ -551,12 +610,7 @@ func newAnthropicContinuationLineage(raw json.RawMessage) (logicalRequestContinu
|
|||
ToolsetDigest: toolsetDigest,
|
||||
}
|
||||
|
||||
committedHistory := map[string]json.RawMessage{
|
||||
"model": fields["model"],
|
||||
"system": fields["system"],
|
||||
"messages": fields["messages"],
|
||||
}
|
||||
committedHistoryDigest, err := fingerprintCanonicalJSON(logicalRequestEndpointAnthropic, committedHistory)
|
||||
committedHistoryDigest, err := fingerprintAnthropicHistory(fields["model"], fields["system"], fields["messages"])
|
||||
if err != nil {
|
||||
return logicalRequestContinuationLineage{}, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue