iop/apps/edge/internal/openai/hot_path_dispatch.go

1238 lines
43 KiB
Go

package openai
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"sort"
"strings"
"time"
edgeservice "iop/apps/edge/internal/service"
"iop/packages/go/config"
iop "iop/proto/gen/iop"
)
func presetSelectorModelGroupKey(dispatch routeDispatch, fallback string) string {
if binding, ok := dispatch.PresetResolvedBindings[dispatch.Preset.Selector.Model]; ok {
if key := binding.effectiveModelGroupKey(dispatch.Preset.Selector.Model); key != "" {
return key
}
}
if model := strings.TrimSpace(dispatch.Preset.Selector.Model); model != "" {
return model
}
return dispatch.effectiveModelGroupKey(fallback)
}
func presetHotPathEnabled(dispatch routeDispatch) bool {
return dispatch.IsPreset && strings.TrimSpace(dispatch.Preset.Selector.Model) != ""
}
// collectPresetSelectorResult consumes the single selected attempt and returns
// both its canonical output and immutable admission evidence. The output is
// never relayed before structural classification.
func (s *Server) collectPresetSelectorResult(
ctx context.Context,
dispatch routeDispatch,
protocol string,
result *edgeservice.ProviderPoolDispatchResult,
) (normalizedStageOutput, hotPathSelectorGate, error) {
if result == nil {
return normalizedStageOutput{}, hotPathSelectorGate{}, fmt.Errorf("preset selector returned no provider result")
}
selected := result.DispatchInfo
gate := hotPathSelectorGate{
PresetID: dispatch.Preset.ID,
SelectorModel: dispatch.Preset.Selector.Model,
ModelGroupKey: selected.ModelGroupKey,
ProviderID: selected.ProviderID,
RunID: selected.RunID,
NodeID: selected.NodeID,
ExecutionPath: selected.ExecutionPath,
ProfileDriver: selected.ProfileDriver,
ProfileCapabilities: append([]string(nil), selected.ProfileCapabilities...),
}
expectedGroup := presetSelectorModelGroupKey(dispatch, dispatch.ExternalModelID)
gate.Healthy = strings.TrimSpace(selected.RunID) != "" &&
strings.TrimSpace(selected.NodeID) != "" &&
strings.TrimSpace(selected.ProviderID) != "" &&
strings.TrimSpace(selected.ModelGroupKey) == strings.TrimSpace(expectedGroup) &&
strings.TrimSpace(selected.ExecutionPath) == string(result.Path)
gate.CapabilitySatisfied = selectedPresetCapability(protocol, selected.ProfileDriver, selected.ProfileCapabilities)
var (
stage normalizedStageOutput
err error
)
switch result.Path {
case edgeservice.ProviderPoolPathNormalized:
stage, err = collectPresetNormalizedResult(ctx, result.Run, selected)
case edgeservice.ProviderPoolPathTunnel:
stage, err = collectPresetTunnelResult(ctx, result.Tunnel, selected, protocol)
default:
err = fmt.Errorf("preset selector returned unsupported execution path %q", result.Path)
}
return stage, gate, err
}
func selectedPresetCapability(protocol, driver string, capabilities []string) bool {
required := "chat"
if protocol == "anthropic" && driver == string(config.ProtocolDriverAnthropicMessages) {
required = "messages"
}
for _, capability := range capabilities {
if strings.TrimSpace(capability) == required {
return true
}
}
return false
}
func collectPresetNormalizedResult(ctx context.Context, handle edgeservice.RunResult, selected edgeservice.RunDispatch) (normalizedStageOutput, error) {
if handle == nil {
return normalizedStageOutput{}, fmt.Errorf("preset selector selected normalized path without a run result")
}
defer handle.Close()
if err := validateSelectedDispatch(selected, handle.Dispatch()); err != nil {
return normalizedStageOutput{}, err
}
stream := handle.Stream()
if stream.Events == nil {
return normalizedStageOutput{}, fmt.Errorf("preset selector run stream is unavailable")
}
timer := time.NewTimer(handle.WaitTimeout())
defer timer.Stop()
stage := normalizedStageOutput{ResponseID: selected.RunID}
var content, reasoning strings.Builder
for {
select {
case <-ctx.Done():
return normalizedStageOutput{}, ctx.Err()
case <-timer.C:
return normalizedStageOutput{}, errRunTimedOut
case nodeEvent, ok := <-stream.NodeEvents:
if !ok {
stream.NodeEvents = nil
continue
}
if edgeservice.IsNodeDisconnected(nodeEvent) {
return normalizedStageOutput{}, fmt.Errorf("node disconnected")
}
case event, ok := <-stream.Events:
if !ok {
return normalizedStageOutput{}, fmt.Errorf("preset selector run stream closed before completion")
}
if event == nil {
continue
}
if event.GetRunId() != "" {
stage.ResponseID = event.GetRunId()
}
if event.GetTimestamp() != 0 {
stage.Created = unixSeconds(event.GetTimestamp())
}
switch event.GetType() {
case "delta":
content.WriteString(event.GetDelta())
case "reasoning_delta":
reasoning.WriteString(event.GetDelta())
case "complete":
stage.Content = content.String()
stage.Reasoning = reasoning.String()
stage.TerminalReason = strings.TrimSpace(event.GetMetadata()["finish_reason"])
if stage.TerminalReason == "" {
stage.TerminalReason = "stop"
}
var err error
stage.ToolCalls, err = normalizeRunEventToolCalls(event.GetMetadata())
if err != nil {
return normalizedStageOutput{}, err
}
if len(stage.ToolCalls) > 0 {
stage.TerminalReason = "tool_calls"
}
if usage := event.GetUsage(); usage != nil {
stage.OpenAIUsage = &openAIUsage{
PromptTokens: int(usage.GetInputTokens()),
CompletionTokens: int(usage.GetOutputTokens()),
TotalTokens: int(usage.GetInputTokens() + usage.GetOutputTokens()),
ReasoningTokens: int(usage.GetReasoningTokens()),
CachedInputTokens: int(usage.GetCachedInputTokens()),
}
stage.Usage, _ = json.Marshal(stage.OpenAIUsage)
}
return stage, nil
case "error", "cancelled":
message := event.GetError()
if message == "" {
message = event.GetMessage()
}
if message == "" {
message = "preset selector run failed"
}
return normalizedStageOutput{}, fmt.Errorf("%s", message)
}
}
}
}
func collectPresetTunnelResult(ctx context.Context, handle edgeservice.ProviderTunnelResult, selected edgeservice.RunDispatch, protocol string) (normalizedStageOutput, error) {
if handle == nil {
return normalizedStageOutput{}, fmt.Errorf("preset selector selected tunnel path without a tunnel result")
}
defer handle.Close()
if err := validateSelectedDispatch(selected, handle.Dispatch()); err != nil {
return normalizedStageOutput{}, err
}
frames := handle.Stream().Frames
if frames == nil {
return normalizedStageOutput{}, fmt.Errorf("preset selector tunnel stream is unavailable")
}
timer := time.NewTimer(handle.WaitTimeout())
defer timer.Stop()
var body bytes.Buffer
status := 0
contentType := ""
var sideUsage *iop.Usage
for {
select {
case <-ctx.Done():
return normalizedStageOutput{}, ctx.Err()
case <-timer.C:
return normalizedStageOutput{}, errRunTimedOut
case frame, ok := <-frames:
if !ok {
return normalizedStageOutput{}, fmt.Errorf("preset selector tunnel closed before completion")
}
if frame == nil {
continue
}
switch frame.GetKind() {
case iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START:
status = int(frame.GetStatusCode())
if status == 0 {
status = http.StatusOK
}
for name, value := range frame.GetHeaders() {
if strings.EqualFold(name, "Content-Type") {
contentType = value
}
}
case iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY:
_, _ = body.Write(frame.GetBody())
case iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_USAGE:
sideUsage = frame.GetUsage()
case iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_ERROR:
message := strings.TrimSpace(frame.GetError())
if message == "" {
message = "provider tunnel failed"
}
return normalizedStageOutput{}, fmt.Errorf("%s", message)
case iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END:
if status < http.StatusOK || status >= http.StatusMultipleChoices {
return normalizedStageOutput{}, fmt.Errorf("preset selector provider returned HTTP %d", status)
}
stage, err := decodePresetTunnelBody(body.Bytes(), contentType, protocol, selected.ProfileDriver)
if err != nil {
return normalizedStageOutput{}, err
}
if err := validateProviderStageMetadata(protocol, stage); err != nil {
return normalizedStageOutput{}, err
}
if len(stage.Usage) == 0 && sideUsage != nil {
stage.OpenAIUsage = &openAIUsage{
PromptTokens: int(sideUsage.GetInputTokens()), CompletionTokens: int(sideUsage.GetOutputTokens()),
TotalTokens: int(sideUsage.GetInputTokens() + sideUsage.GetOutputTokens()),
ReasoningTokens: int(sideUsage.GetReasoningTokens()), CachedInputTokens: int(sideUsage.GetCachedInputTokens()),
}
if protocol == "anthropic" && selected.ProfileDriver == string(config.ProtocolDriverAnthropicMessages) {
stage.Usage, _ = json.Marshal(anthropicUsage{
InputTokens: int(sideUsage.GetInputTokens()), OutputTokens: int(sideUsage.GetOutputTokens()),
CacheReadInputTokens: int(sideUsage.GetCachedInputTokens()),
})
} else if protocol == "anthropic" {
stage.Usage = openAIUsageToAnthropic(mustMarshalRaw(stage.OpenAIUsage))
} else {
stage.Usage, _ = json.Marshal(stage.OpenAIUsage)
}
}
return stage, nil
}
}
}
}
func validateProviderStageMetadata(protocol string, stage normalizedStageOutput) error {
if strings.TrimSpace(stage.ResponseID) == "" {
return fmt.Errorf("provider response is missing required identity")
}
return nil
}
func validateSelectedDispatch(selected, handle edgeservice.RunDispatch) error {
if handle.ProviderID != "" && selected.ProviderID != handle.ProviderID {
return fmt.Errorf("preset selector dispatch evidence changed after admission")
}
if handle.ModelGroupKey != "" && selected.ModelGroupKey != handle.ModelGroupKey {
return fmt.Errorf("preset selector dispatch evidence changed after admission")
}
return nil
}
func unixSeconds(timestamp int64) int64 {
if timestamp > 1_000_000_000_000 {
return timestamp / int64(time.Second)
}
return timestamp
}
func decodePresetTunnelBody(body []byte, contentType, protocol, driver string) (normalizedStageOutput, error) {
streaming := strings.Contains(strings.ToLower(contentType), "text/event-stream") || bytes.Contains(body, []byte("data:"))
if protocol == "anthropic" && driver == string(config.ProtocolDriverAnthropicMessages) {
if streaming {
return decodeAnthropicPresetSSE(body)
}
return decodeAnthropicPresetJSON(body)
}
var stage normalizedStageOutput
var err error
if streaming {
stage, err = decodeOpenAIPresetSSE(body)
} else {
stage, err = decodeOpenAIPresetJSON(body)
}
if err != nil {
return normalizedStageOutput{}, err
}
if protocol == "anthropic" {
stage.Usage = openAIUsageToAnthropic(stage.Usage)
stage.TerminalReason = openAIReasonToAnthropic(stage.TerminalReason)
}
return stage, nil
}
func decodeOpenAIPresetJSON(body []byte) (normalizedStageOutput, error) {
var response struct {
ID string `json:"id"`
Created int64 `json:"created"`
Usage json.RawMessage `json:"usage"`
Choices []struct {
Message struct {
Content any `json:"content"`
ReasoningContent string `json:"reasoning_content"`
Reasoning string `json:"reasoning"`
ToolCalls []any `json:"tool_calls"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
}
if err := json.Unmarshal(body, &response); err != nil {
return normalizedStageOutput{}, fmt.Errorf("decode preset Chat response: %w", err)
}
if len(response.Choices) != 1 {
return normalizedStageOutput{}, fmt.Errorf("preset Chat response must contain exactly one choice")
}
choice := response.Choices[0]
reasoning := choice.Message.ReasoningContent
if reasoning == "" {
reasoning = choice.Message.Reasoning
}
toolCalls, err := normalizeProviderToolCalls(choice.Message.ToolCalls)
if err != nil {
return normalizedStageOutput{}, err
}
stage := normalizedStageOutput{
ResponseID: response.ID, Created: response.Created, Content: contentToString(choice.Message.Content),
Reasoning: reasoning, ToolCalls: toolCalls,
TerminalReason: choice.FinishReason, Usage: cloneRawJSON(response.Usage),
}
stage.OpenAIUsage = decodeOpenAIUsage(response.Usage)
return stage, nil
}
func decodeOpenAIPresetSSE(body []byte) (normalizedStageOutput, error) {
stage := normalizedStageOutput{}
type toolState struct {
id, name string
args strings.Builder
}
tools := make(map[int]*toolState)
for _, payload := range sseDataPayloads(body) {
if bytes.Equal(bytes.TrimSpace(payload), []byte("[DONE]")) {
continue
}
var chunk openAIChatStreamChunk
if err := json.Unmarshal(payload, &chunk); err != nil {
return normalizedStageOutput{}, fmt.Errorf("decode preset Chat stream: %w", err)
}
if chunk.Error != nil {
return normalizedStageOutput{}, fmt.Errorf("preset Chat stream error: %s", chunk.Error.Message)
}
if chunk.ID != "" {
stage.ResponseID = chunk.ID
}
var raw struct {
Created int64 `json:"created"`
Usage json.RawMessage `json:"usage"`
}
_ = json.Unmarshal(payload, &raw)
if raw.Created != 0 {
stage.Created = raw.Created
}
if len(raw.Usage) > 0 && string(raw.Usage) != "null" {
stage.Usage = cloneRawJSON(raw.Usage)
stage.OpenAIUsage = decodeOpenAIUsage(raw.Usage)
}
for _, choice := range chunk.Choices {
stage.Content += choice.Delta.Content
reasoning := choice.Delta.ReasoningContent
if reasoning == "" {
reasoning = choice.Delta.Reasoning
}
stage.Reasoning += reasoning
for _, delta := range choice.Delta.ToolCalls {
state := tools[delta.Index]
if state == nil {
state = &toolState{}
tools[delta.Index] = state
}
if delta.ID != "" {
state.id = delta.ID
}
if delta.Function.Name != "" {
state.name = delta.Function.Name
}
state.args.WriteString(delta.Function.Arguments)
}
if choice.FinishReason != nil {
stage.TerminalReason = *choice.FinishReason
}
}
}
for index := 0; index < len(tools); index++ {
state, ok := tools[index]
if !ok {
return normalizedStageOutput{}, fmt.Errorf("preset Chat stream tool indices are not contiguous")
}
call, err := normalizedToolCallFromParts(state.id, state.name, state.args.String())
if err != nil {
return normalizedStageOutput{}, err
}
stage.ToolCalls = append(stage.ToolCalls, call)
}
return stage, nil
}
func decodeAnthropicPresetJSON(body []byte) (normalizedStageOutput, error) {
var response struct {
ID string `json:"id"`
Content []json.RawMessage `json:"content"`
StopReason string `json:"stop_reason"`
Usage json.RawMessage `json:"usage"`
}
if err := json.Unmarshal(body, &response); err != nil {
return normalizedStageOutput{}, fmt.Errorf("decode preset Messages response: %w", err)
}
stage := normalizedStageOutput{ResponseID: response.ID, TerminalReason: response.StopReason, Usage: cloneRawJSON(response.Usage)}
for _, raw := range response.Content {
if err := appendAnthropicBlock(&stage, raw); err != nil {
return normalizedStageOutput{}, err
}
}
return stage, nil
}
func decodeAnthropicPresetSSE(body []byte) (normalizedStageOutput, error) {
stage := normalizedStageOutput{}
type toolState struct {
id, name string
args strings.Builder
}
tools := make(map[int]*toolState)
for _, payload := range sseDataPayloads(body) {
var event map[string]json.RawMessage
if err := json.Unmarshal(payload, &event); err != nil {
return normalizedStageOutput{}, fmt.Errorf("decode preset Messages stream: %w", err)
}
var eventType string
_ = json.Unmarshal(event["type"], &eventType)
switch eventType {
case "message_start":
var message struct {
ID string `json:"id"`
Usage json.RawMessage `json:"usage"`
}
if err := json.Unmarshal(event["message"], &message); err != nil {
return normalizedStageOutput{}, fmt.Errorf("decode preset Messages start: %w", err)
}
stage.ResponseID = message.ID
stage.Usage = mergeJSONObjects(stage.Usage, message.Usage)
case "content_block_start":
var start struct {
Index int `json:"index"`
Block struct {
Type, ID, Name, Text, Thinking, Signature string
Input json.RawMessage `json:"input"`
} `json:"content_block"`
}
if err := json.Unmarshal(payload, &start); err != nil {
return normalizedStageOutput{}, err
}
switch start.Block.Type {
case "text":
stage.Content += start.Block.Text
case "thinking":
stage.Reasoning += start.Block.Thinking
stage.ReasoningSignature += start.Block.Signature
case "tool_use":
state := &toolState{id: start.Block.ID, name: start.Block.Name}
if len(start.Block.Input) > 0 && string(start.Block.Input) != "{}" {
state.args.Write(start.Block.Input)
}
tools[start.Index] = state
}
case "content_block_delta":
var delta struct {
Index int `json:"index"`
Delta struct {
Type, Text, Thinking, Signature, PartialJSON string
} `json:"delta"`
}
if err := json.Unmarshal(payload, &delta); err != nil {
return normalizedStageOutput{}, err
}
switch delta.Delta.Type {
case "text_delta":
stage.Content += delta.Delta.Text
case "thinking_delta":
stage.Reasoning += delta.Delta.Thinking
case "signature_delta":
stage.ReasoningSignature += delta.Delta.Signature
case "input_json_delta":
if state := tools[delta.Index]; state != nil {
state.args.WriteString(delta.Delta.PartialJSON)
}
}
case "message_delta":
var delta struct {
Delta struct {
StopReason string `json:"stop_reason"`
} `json:"delta"`
Usage json.RawMessage `json:"usage"`
}
if err := json.Unmarshal(payload, &delta); err != nil {
return normalizedStageOutput{}, err
}
stage.TerminalReason = delta.Delta.StopReason
stage.Usage = mergeJSONObjects(stage.Usage, delta.Usage)
case "error":
return normalizedStageOutput{}, fmt.Errorf("preset Messages stream returned an error")
}
}
indices := make([]int, 0, len(tools))
for index := range tools {
indices = append(indices, index)
}
sort.Ints(indices)
for _, index := range indices {
state := tools[index]
args := state.args.String()
if args == "" {
args = "{}"
}
call, err := normalizedToolCallFromParts(state.id, state.name, args)
if err != nil {
return normalizedStageOutput{}, err
}
stage.ToolCalls = append(stage.ToolCalls, call)
}
return stage, nil
}
func appendAnthropicBlock(stage *normalizedStageOutput, raw json.RawMessage) error {
var block struct {
Type, Text, Thinking, Signature, ID, Name string
Input json.RawMessage `json:"input"`
}
if err := json.Unmarshal(raw, &block); err != nil {
return fmt.Errorf("decode preset Messages content block: %w", err)
}
switch block.Type {
case "text":
stage.Content += block.Text
case "thinking":
stage.Reasoning += block.Thinking
stage.ReasoningSignature += block.Signature
case "tool_use":
call, err := normalizedToolCallFromParts(block.ID, block.Name, string(block.Input))
if err != nil {
return err
}
stage.ToolCalls = append(stage.ToolCalls, call)
}
return nil
}
func normalizeProviderToolCalls(toolCalls []any) ([]normalizedToolCall, error) {
out := make([]normalizedToolCall, 0, len(toolCalls))
for _, value := range toolCalls {
raw, err := json.Marshal(value)
if err != nil {
return nil, fmt.Errorf("encode preset selector tool call: %w", err)
}
var call struct {
ID string `json:"id"`
Name string `json:"name"`
Input json.RawMessage `json:"input"`
Function struct {
Name string `json:"name"`
Arguments any `json:"arguments"`
} `json:"function"`
}
if err := json.Unmarshal(raw, &call); err != nil {
return nil, fmt.Errorf("decode preset selector tool call: %w", err)
}
name := call.Function.Name
if name == "" {
name = call.Name
}
arguments := call.Function.Arguments
if arguments == nil && len(call.Input) > 0 {
arguments = call.Input
}
var rawArgs []byte
switch typed := arguments.(type) {
case string:
rawArgs = []byte(typed)
case json.RawMessage:
rawArgs = typed
default:
rawArgs, _ = json.Marshal(typed)
}
normalized, err := normalizedToolCallFromParts(call.ID, name, string(rawArgs))
if err != nil {
return nil, err
}
out = append(out, normalized)
}
return out, nil
}
func normalizeRunEventToolCalls(metadata map[string]string) ([]normalizedToolCall, error) {
raw := strings.TrimSpace(metadata[runtimeMetadataOpenAIToolCalls])
if raw == "" {
return nil, nil
}
var calls []any
decoder := json.NewDecoder(strings.NewReader(raw))
decoder.UseNumber()
if err := decoder.Decode(&calls); err != nil {
return nil, fmt.Errorf("decode preset selector run tool calls: %w", err)
}
if err := requireJSONEOF(decoder); err != nil {
return nil, fmt.Errorf("decode preset selector run tool calls: %w", err)
}
return normalizeProviderToolCalls(calls)
}
func normalizedToolCallFromParts(id, name, rawArgs string) (normalizedToolCall, error) {
if strings.TrimSpace(id) == "" || strings.TrimSpace(name) == "" {
return normalizedToolCall{}, fmt.Errorf("preset selector tool call requires id and name")
}
if strings.TrimSpace(rawArgs) == "" {
rawArgs = "{}"
}
var arguments map[string]any
decoder := json.NewDecoder(strings.NewReader(rawArgs))
decoder.UseNumber()
if err := decoder.Decode(&arguments); err != nil || arguments == nil {
return normalizedToolCall{}, fmt.Errorf("preset selector tool call %q has invalid arguments", id)
}
if err := requireJSONEOF(decoder); err != nil {
return normalizedToolCall{}, fmt.Errorf("preset selector tool call %q has invalid arguments", id)
}
return normalizedToolCall{ID: id, ProviderCallID: id, Name: name, Arguments: arguments, RawArgs: rawArgs}, nil
}
func requireJSONEOF(decoder *json.Decoder) error {
var extra any
if err := decoder.Decode(&extra); err != io.EOF {
if err == nil {
return fmt.Errorf("multiple JSON values")
}
return err
}
return nil
}
func mustMarshalRaw(value any) json.RawMessage {
raw, _ := json.Marshal(value)
return raw
}
func sseDataPayloads(body []byte) [][]byte {
normalized := bytes.ReplaceAll(body, []byte("\r\n"), []byte("\n"))
events := bytes.Split(normalized, []byte("\n\n"))
var payloads [][]byte
for _, event := range events {
var lines [][]byte
for _, line := range bytes.Split(event, []byte("\n")) {
line = bytes.TrimSpace(line)
if bytes.HasPrefix(line, []byte("data:")) {
lines = append(lines, bytes.TrimSpace(bytes.TrimPrefix(line, []byte("data:"))))
}
}
if len(lines) > 0 {
payloads = append(payloads, bytes.Join(lines, []byte("\n")))
}
}
return payloads
}
func cloneRawJSON(raw json.RawMessage) json.RawMessage {
if len(raw) == 0 || string(raw) == "null" {
return nil
}
return append(json.RawMessage(nil), raw...)
}
func decodeOpenAIUsage(raw json.RawMessage) *openAIUsage {
if len(raw) == 0 || string(raw) == "null" {
return nil
}
var usage openAIUsage
if json.Unmarshal(raw, &usage) != nil {
return nil
}
return &usage
}
func openAIUsageToAnthropic(raw json.RawMessage) json.RawMessage {
if len(raw) == 0 {
return nil
}
var usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
PromptDetails struct {
CachedTokens int `json:"cached_tokens"`
} `json:"prompt_tokens_details"`
}
if json.Unmarshal(raw, &usage) != nil {
return nil
}
converted, _ := json.Marshal(anthropicUsage{
InputTokens: usage.PromptTokens, OutputTokens: usage.CompletionTokens,
CacheReadInputTokens: usage.PromptDetails.CachedTokens,
})
return converted
}
func openAIReasonToAnthropic(reason string) string {
switch reason {
case "tool_calls", "function_call":
return "tool_use"
case "length":
return "max_tokens"
case "stop", "":
return "end_turn"
default:
return reason
}
}
func mergeJSONObjects(left, right json.RawMessage) json.RawMessage {
values := make(map[string]any)
if len(left) > 0 {
_ = json.Unmarshal(left, &values)
}
if len(right) > 0 {
var extra map[string]any
if json.Unmarshal(right, &extra) == nil {
for key, value := range extra {
values[key] = value
}
}
}
if len(values) == 0 {
return nil
}
merged, _ := json.Marshal(values)
return merged
}
func (s *Server) dispatchPresetTurn(
w http.ResponseWriter,
r *http.Request,
dispatch routeDispatch,
protocol string,
stream bool,
runMeta map[string]string,
output normalizedStageOutput,
gate hotPathSelectorGate,
) error {
requestID := runMeta["iop_logical_request_id"]
stageID := runMeta["iop_stage_id"]
callID := runMeta["iop_call_id"]
ownerEdgeID := s.edgeIDValue()
issued := newReservedPaths(requestID)
preset := dispatch.Preset
if preset.ID == "" {
if found, ok := s.ExecutionPreset(dispatch.PresetID); ok {
preset = found
}
}
decision, err := classifyHotPathOutput(preset, issued, output, gate)
if err != nil {
s.terminalPresetRequest(requestID, ownerEdgeID)
if protocol == "anthropic" {
writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", err.Error())
} else {
writeError(w, http.StatusBadRequest, "invalid_request_error", err.Error())
}
return err
}
if s.artifactFrontiers.pairRequired(requestID, ownerEdgeID) && decision.Mode != modeLight {
s.terminalPresetRequest(requestID, ownerEdgeID)
err := fmt.Errorf("artifact frontier requires the exact Plan/Review pair before local-stage handoff")
if protocol == "anthropic" {
writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", err.Error())
} else {
writeError(w, http.StatusBadRequest, "invalid_request_error", err.Error())
}
return err
}
switch decision.Mode {
case modeDirect:
turn := &hotPathTurn{
RequestID: requestID, StageID: stageID, CallID: callID, OwnerEdgeID: ownerEdgeID,
PrincipalRef: runMeta[principalMetaRef], Preset: preset, Dispatch: dispatch,
Protocol: protocol, Stream: stream, PublicModelID: dispatch.ExternalModelID,
Writer: w, Request: r,
}
return s.runDirectTurn(r.Context(), turn, output)
case modeLight:
turn := &hotPathTurn{
RequestID: requestID, StageID: stageID, CallID: callID, OwnerEdgeID: ownerEdgeID,
PrincipalRef: runMeta[principalMetaRef], Preset: preset, Dispatch: dispatch,
Protocol: protocol, Stream: stream, PublicModelID: dispatch.ExternalModelID,
Writer: w, Request: r,
}
return s.runArtifactPairTurn(turn, output, gate)
default:
s.terminalPresetRequest(requestID, ownerEdgeID)
errMsg := fmt.Sprintf("unsupported mode %q", decision.Mode)
if protocol == "anthropic" {
writeAnthropicError(w, http.StatusBadRequest, "invalid_request_error", errMsg)
} else {
writeError(w, http.StatusBadRequest, "invalid_request_error", errMsg)
}
return fmt.Errorf("%s", errMsg)
}
}
func (s *Server) submitHotPathStage(ctx context.Context, r *http.Request, snapshot hotPathDispatchSnapshot) (normalizedStageOutput, hotPathStageCorrelation, error) {
if err := snapshot.Input.validate(); err != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, err
}
prompt, err := snapshot.Input.prompt(snapshot.Phase)
if err != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, err
}
route, err := s.revalidateHotPathStageRoute(ctx, snapshot)
if err != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, err
}
modelGroupKey := route.effectiveModelGroupKey(snapshot.Stage.Model)
metadata := map[string]string{
"iop_logical_request_id": snapshot.RequestID,
"iop_stage_id": snapshot.StageID,
"iop_stage_role": snapshot.Input.Role,
}
if snapshot.PrincipalRef != "" {
metadata[principalMetaRef] = snapshot.PrincipalRef
}
applyTrustedManagedBindingMetadata(metadata, route)
estimate := estimateInputTokensBytes([]byte(prompt), metadata, snapshot.Tools, nil)
contextClass := classifyContext(estimate, s.longContextThreshold())
runInput := hotPathStageRunInput(snapshot, prompt)
runReq := edgeservice.SubmitRunRequest{
NodeRef: route.NodeRef, ModelGroupKey: modelGroupKey, ProviderID: route.ProviderID,
UsageAttribution: route.UsageAttribution, Adapter: route.Adapter, Target: route.Target,
SessionID: route.SessionID, Prompt: prompt, Input: runInput, TimeoutSec: route.TimeoutSec,
MaxQueue: route.MaxQueue, QueueTimeoutMS: route.QueueTimeoutMS, Metadata: metadata,
EstimatedInputTokens: estimate, ContextClass: contextClass, ProviderPool: route.ProviderPool,
}
if !route.ProviderPool {
if routeUsesProviderTunnel(route) {
tunnelReq := hotPathStageTunnelRequest(snapshot, route, modelGroupKey, metadata, estimate, contextClass)
tunnelReq.Operation = string(config.OperationChatCompletions)
tunnelReq.Path = "/v1/chat/completions"
tunnelReq.BuildBody = func(target string) ([]byte, error) {
return hotPathChatStageBody(snapshot, prompt, target)
}
headers, headerErr := s.providerTunnelAuthHeaders(r)
if headerErr != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, headerErr
}
tunnelReq.Headers = headers
handle, submitErr := s.service.SubmitProviderTunnel(ctx, tunnelReq)
if submitErr != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, submitErr
}
dispatch := handle.Dispatch()
output, collectErr := collectPresetTunnelResult(ctx, handle, dispatch, "openai")
if collectErr != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, collectErr
}
return output, stageCorrelation(snapshot.StageID, output, dispatch), nil
}
handle, submitErr := s.service.SubmitRun(ctx, runReq)
if submitErr != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, submitErr
}
dispatch := handle.Dispatch()
output, collectErr := collectPresetNormalizedResult(ctx, handle, dispatch)
if collectErr != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, collectErr
}
return output, stageCorrelation(snapshot.StageID, output, dispatch), nil
}
poolReq := edgeservice.ProviderPoolDispatchRequest{
Run: runReq,
Tunnel: hotPathStageTunnelRequest(snapshot, route, modelGroupKey, metadata, estimate, contextClass),
}
poolReq.AcceptCandidate = hotPathStageCandidatePredicate(snapshot)
if route.Managed {
poolReq.AcceptCandidate = composeCandidatePredicates(poolReq.AcceptCandidate, route.CandidatePredicate())
}
poolReq.PrepareProtocolTunnel = s.prepareHotPathStageTunnel(r, snapshot, prompt)
result, err := s.service.SubmitProviderPool(ctx, poolReq)
if err != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, err
}
if result == nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, fmt.Errorf("hot path stage returned no provider result")
}
if err := validateHotPathStageDispatch(snapshot, route, result.DispatchInfo); err != nil {
if result.Run != nil {
result.Run.Close()
}
if result.Tunnel != nil {
result.Tunnel.Close()
}
return normalizedStageOutput{}, hotPathStageCorrelation{}, err
}
var output normalizedStageOutput
switch result.Path {
case edgeservice.ProviderPoolPathNormalized:
output, err = collectPresetNormalizedResult(ctx, result.Run, result.DispatchInfo)
case edgeservice.ProviderPoolPathTunnel:
wireProtocol := "openai"
if result.DispatchInfo.ProfileDriver == string(config.ProtocolDriverAnthropicMessages) {
wireProtocol = "anthropic"
}
output, err = collectPresetTunnelResult(ctx, result.Tunnel, result.DispatchInfo, wireProtocol)
default:
err = fmt.Errorf("hot path stage returned unsupported execution path %q", result.Path)
}
if err != nil {
return normalizedStageOutput{}, hotPathStageCorrelation{}, err
}
if strings.TrimSpace(output.ResponseID) == "" {
return normalizedStageOutput{}, hotPathStageCorrelation{}, fmt.Errorf("hot path stage completion is missing provider identity")
}
return output, stageCorrelation(snapshot.StageID, output, result.DispatchInfo), nil
}
func hotPathStageTunnelRequest(snapshot hotPathDispatchSnapshot, route routeDispatch, modelGroupKey string, metadata map[string]string, estimate int, contextClass string) edgeservice.SubmitProviderTunnelRequest {
return edgeservice.SubmitProviderTunnelRequest{
CredentialBinding: route.credentialBinding(), ModelGroupKey: modelGroupKey,
ProviderID: route.ProviderID, UsageAttribution: route.UsageAttribution,
SessionID: route.SessionID, Method: http.MethodPost, Stream: snapshot.Stream,
TimeoutSec: route.TimeoutSec, MaxQueue: route.MaxQueue, QueueTimeoutMS: route.QueueTimeoutMS,
Metadata: metadata, EstimatedInputTokens: estimate, ContextClass: contextClass, ProviderPool: route.ProviderPool,
}
}
func (s *Server) prepareHotPathStageTunnel(r *http.Request, snapshot hotPathDispatchSnapshot, prompt string) func(edgeservice.SubmitProviderTunnelRequest, edgeservice.ProviderPoolCandidate) (edgeservice.SubmitProviderTunnelRequest, error) {
return func(tunnelReq edgeservice.SubmitProviderTunnelRequest, selected edgeservice.ProviderPoolCandidate) (edgeservice.SubmitProviderTunnelRequest, error) {
if selected.ProtocolProfile == nil {
headers, err := s.providerTunnelAuthHeaders(r)
if err != nil {
return tunnelReq, err
}
tunnelReq.Headers = headers
tunnelReq.Path = "/v1/chat/completions"
tunnelReq.Operation = string(config.OperationChatCompletions)
tunnelReq.BuildBody = func(target string) ([]byte, error) {
return hotPathChatStageBody(snapshot, prompt, target)
}
return tunnelReq, nil
}
profile := selected.ProtocolProfile.Clone()
switch profile.Driver {
case config.ProtocolDriverOpenAIChat:
prepared, err := s.protocolTunnelPreparer(r, config.OperationChatCompletions)(tunnelReq, selected)
if err != nil {
return tunnelReq, err
}
prepared.Path = "/v1/chat/completions"
prepared.BuildBody = func(target string) ([]byte, error) {
return hotPathChatStageBody(snapshot, prompt, target)
}
return prepared, nil
case config.ProtocolDriverAnthropicMessages:
request := r.Clone(r.Context())
if strings.TrimSpace(request.Header.Get(anthropicVersionHeader)) == "" {
request.Header.Set(anthropicVersionHeader, anthropicSupportedVersion)
}
headers, err := s.anthropicUpstreamHeaders(request, profile, true)
if err != nil {
return tunnelReq, err
}
tunnelReq.Headers = headers
tunnelReq.Path = "/v1/messages"
tunnelReq.Operation = string(config.OperationMessages)
tunnelReq.BuildBody = func(target string) ([]byte, error) {
return hotPathAnthropicStageBody(snapshot, prompt, target)
}
return tunnelReq, nil
default:
return tunnelReq, fmt.Errorf("hot path stage does not support protocol driver %q", profile.Driver)
}
}
}
func hotPathStageCandidatePredicate(snapshot hotPathDispatchSnapshot) edgeservice.ProviderPoolCandidatePredicate {
needsTools := len(snapshot.Tools) > 0
return func(candidate edgeservice.ProviderPoolCandidate) bool {
if candidate.ExecutionPath == string(edgeservice.ProviderPoolPathNormalized) {
return true
}
profile := candidate.ProtocolProfile
if profile == nil {
return true
}
if snapshot.Stream && !profile.HasCapability("streaming") {
return false
}
if needsTools && !profile.HasCapability("tool_calling") {
return false
}
switch profile.Driver {
case config.ProtocolDriverOpenAIChat:
return profile.HasCapability("chat") && profileHasOperation(*profile, config.OperationChatCompletions)
case config.ProtocolDriverAnthropicMessages:
return profile.HasCapability("messages") && profileHasOperation(*profile, config.OperationMessages)
default:
return false
}
}
}
func (s *Server) revalidateHotPathStageRoute(ctx context.Context, snapshot hotPathDispatchSnapshot) (routeDispatch, error) {
pinned := snapshot.Route
if !pinned.Managed {
return pinned, nil
}
currentPreset, err := s.resolveRouteDispatchForPrincipal(ctx, snapshot.PresetRoute.ExternalModelID)
if err != nil {
return routeDispatch{}, fmt.Errorf("revalidate hot path stage route: %w", err)
}
current, ok := currentPreset.PresetResolvedBindings[snapshot.Stage.Model]
if !ok || !samePinnedHotPathRoute(pinned, current) {
return routeDispatch{}, fmt.Errorf("hot path stage route or credential revision changed")
}
return current, nil
}
func samePinnedHotPathRoute(left, right routeDispatch) bool {
return left.Managed == right.Managed && left.PrincipalRef == right.PrincipalRef &&
left.ModelGroupKey == right.ModelGroupKey && left.RouteID == right.RouteID &&
left.CredentialSlotRef == right.CredentialSlotRef && left.ProfileID == right.ProfileID &&
left.UpstreamModel == right.UpstreamModel && left.ResourceSelector == right.ResourceSelector &&
left.RouteRevision == right.RouteRevision && left.CredentialRevision == right.CredentialRevision &&
left.ProjectionGeneration == right.ProjectionGeneration
}
func validateHotPathStageDispatch(snapshot hotPathDispatchSnapshot, route routeDispatch, selected edgeservice.RunDispatch) error {
if strings.TrimSpace(selected.RunID) == "" || strings.TrimSpace(selected.NodeID) == "" || strings.TrimSpace(selected.ProviderID) == "" {
return fmt.Errorf("hot path stage dispatch correlation is incomplete")
}
if selected.ModelGroupKey != route.effectiveModelGroupKey(snapshot.Stage.Model) {
return fmt.Errorf("hot path stage model binding changed after admission")
}
if route.ProviderID != "" && selected.ProviderID != route.ProviderID {
return fmt.Errorf("hot path stage provider binding changed after admission")
}
return nil
}
func stageCorrelation(stageID string, output normalizedStageOutput, dispatch edgeservice.RunDispatch) hotPathStageCorrelation {
return hotPathStageCorrelation{
StageID: stageID, ResponseID: output.ResponseID, RunID: dispatch.RunID,
ProviderID: dispatch.ProviderID, Terminal: output.TerminalReason,
}
}
func hotPathStageRunInput(snapshot hotPathDispatchSnapshot, prompt string) map[string]any {
messages := hotPathChatStageMessages(snapshot, prompt)
input := map[string]any{"prompt": prompt, "messages": messages}
if tools := hotPathChatTools(snapshot.Tools); len(tools) > 0 {
input["tools"] = tools
input["tool_choice"] = "auto"
}
if len(snapshot.Stage.Options) > 0 {
input["options"] = cloneAnyMap(snapshot.Stage.Options)
}
return input
}
func hotPathChatStageBody(snapshot hotPathDispatchSnapshot, prompt, target string) ([]byte, error) {
body := map[string]any{
"model": target, "messages": hotPathChatStageMessages(snapshot, prompt), "stream": snapshot.Stream,
}
if tools := hotPathChatTools(snapshot.Tools); len(tools) > 0 {
body["tools"] = tools
body["tool_choice"] = "auto"
}
applyHotPathStageOptions(body, snapshot.Stage.Options, map[string]struct{}{"model": {}, "messages": {}, "tools": {}, "stream": {}})
return json.Marshal(body)
}
func hotPathAnthropicStageBody(snapshot hotPathDispatchSnapshot, prompt, target string) ([]byte, error) {
body := map[string]any{
"model": target, "max_tokens": 4096, "messages": hotPathAnthropicStageMessages(snapshot, prompt), "stream": snapshot.Stream,
}
if tools := hotPathAnthropicTools(snapshot.Tools); len(tools) > 0 {
body["tools"] = tools
body["tool_choice"] = map[string]any{"type": "auto"}
}
applyHotPathStageOptions(body, snapshot.Stage.Options, map[string]struct{}{"model": {}, "messages": {}, "tools": {}, "stream": {}})
return json.Marshal(body)
}
func applyHotPathStageOptions(body map[string]any, options map[string]any, reserved map[string]struct{}) {
for key, value := range options {
if _, blocked := reserved[key]; blocked {
continue
}
body[key] = cloneAnyValue(value)
}
}
func hotPathChatStageMessages(snapshot hotPathDispatchSnapshot, prompt string) []any {
messages := []any{map[string]any{"role": "user", "content": prompt}}
for _, exchange := range snapshot.Transcript {
assistant := map[string]any{"role": "assistant", "content": exchange.Output.Content}
if exchange.Output.Reasoning != "" {
assistant["reasoning_content"] = exchange.Output.Reasoning
}
if len(exchange.Output.ToolCalls) > 0 {
calls := make([]any, 0, len(exchange.Output.ToolCalls))
for _, call := range exchange.Output.ToolCalls {
providerID := call.ProviderCallID
if providerID == "" {
providerID = call.ID
}
calls = append(calls, map[string]any{
"id": providerID, "type": "function",
"function": map[string]any{"name": call.Name, "arguments": directToolArguments(call)},
})
}
assistant["tool_calls"] = calls
}
messages = append(messages, assistant)
for _, result := range exchange.Results {
messages = append(messages, map[string]any{
"role": "tool", "tool_call_id": result.ProviderCallID, "content": result.Body,
})
}
}
return messages
}
func hotPathAnthropicStageMessages(snapshot hotPathDispatchSnapshot, prompt string) []any {
messages := []any{map[string]any{"role": "user", "content": prompt}}
for _, exchange := range snapshot.Transcript {
blocks := anthropicDirectBlocks(exchange.Output)
for _, block := range blocks {
if block["type"] == "tool_use" {
for _, call := range exchange.Output.ToolCalls {
if block["id"] == call.ID && call.ProviderCallID != "" {
block["id"] = call.ProviderCallID
}
}
}
}
messages = append(messages, map[string]any{"role": "assistant", "content": blocks})
results := make([]any, 0, len(exchange.Results))
for _, result := range exchange.Results {
results = append(results, map[string]any{
"type": "tool_result", "tool_use_id": result.ProviderCallID,
"content": result.Body, "is_error": result.IsError,
})
}
messages = append(messages, map[string]any{"role": "user", "content": results})
}
return messages
}
func hotPathChatTools(tools []any) []any {
schemas, _ := normalizeToolSchemas(tools)
names := make([]string, 0, len(schemas))
for name := range schemas {
names = append(names, name)
}
sort.Strings(names)
out := make([]any, 0, len(names))
for _, name := range names {
schema := schemas[name]
function := map[string]any{"name": schema.name, "parameters": cloneAnyMap(schema.schema)}
if schema.description != "" {
function["description"] = schema.description
}
out = append(out, map[string]any{"type": "function", "function": function})
}
return out
}
func hotPathAnthropicTools(tools []any) []any {
schemas, _ := normalizeToolSchemas(tools)
names := make([]string, 0, len(schemas))
for name := range schemas {
names = append(names, name)
}
sort.Strings(names)
out := make([]any, 0, len(names))
for _, name := range names {
schema := schemas[name]
tool := map[string]any{"name": schema.name, "input_schema": cloneAnyMap(schema.schema)}
if schema.description != "" {
tool["description"] = schema.description
}
out = append(out, tool)
}
return out
}
func (s *Server) terminalPresetRequest(requestID, ownerEdgeID string) {
if requestID != "" {
if s.lightFlows != nil {
s.lightFlows.remove(requestID, ownerEdgeID)
}
if s.artifactFrontiers != nil {
s.artifactFrontiers.remove(requestID, ownerEdgeID)
}
_ = s.requestCoordinator.terminal(requestID, ownerEdgeID)
}
}