Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
|
|
@ -0,0 +1,988 @@
|
|||
package multiagentv2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/home"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
const (
|
||||
codexSpawnAgentDescriptionMarker = "Spawns an agent"
|
||||
codexSpawnAgentModelsHeading = "Available model overrides (optional; inherited parent model is preferred):"
|
||||
codexCollaborationNamespace = "collaboration"
|
||||
codexOptimizedCollaborationNamespace = "collaboration-optimize"
|
||||
codexOptimizedCollaborationNamePrefix = codexOptimizedCollaborationNamespace + "__"
|
||||
)
|
||||
|
||||
// CodexMultiAgentV2ToolsPreparedContextKey marks a request whose collaboration
|
||||
// tool definitions were prepared at the Responses API boundary.
|
||||
const CodexMultiAgentV2ToolsPreparedContextKey = "codex_multi_agent_v2_tools_prepared"
|
||||
|
||||
// codexCollaborationMessageTools are the collaboration tool names whose
|
||||
// parameters.properties.message.encrypted field must be stripped so that
|
||||
// message content remains readable by the proxy.
|
||||
var codexCollaborationMessageTools = map[string]struct{}{
|
||||
"spawn_agent": {},
|
||||
"send_message": {},
|
||||
"followup_task": {},
|
||||
}
|
||||
|
||||
type codexSpawnAgentModel struct {
|
||||
id string
|
||||
description string
|
||||
reasoningEfforts []string
|
||||
defaultReasoningEffort string
|
||||
serviceTiers []string
|
||||
priority int
|
||||
displayName string
|
||||
}
|
||||
|
||||
type codexClientModelsCatalog struct {
|
||||
Models []map[string]any `json:"models"`
|
||||
}
|
||||
|
||||
// RewriteCodexSpawnAgentDescription optimizes spawn_agent definitions for
|
||||
// official Codex clients when multi-agent v2 optimization is enabled.
|
||||
func RewriteCodexSpawnAgentDescription(ctx context.Context, headers http.Header, payload []byte, cfg *config.Config) []byte {
|
||||
updated, _ := OptimizeCodexMultiAgentV2Request(ctx, headers, payload, cfg)
|
||||
return updated
|
||||
}
|
||||
|
||||
// RewriteCodexMultiAgentV2Input converts official Codex multi-agent input into
|
||||
// standard Responses API messages when multi-agent v2 optimization is enabled.
|
||||
func RewriteCodexMultiAgentV2Input(ctx context.Context, headers http.Header, payload []byte, cfg *config.Config) []byte {
|
||||
if !codexMultiAgentV2Enabled(ctx, headers, cfg) {
|
||||
return payload
|
||||
}
|
||||
return rewriteCodexAgentMessageInput(payload)
|
||||
}
|
||||
|
||||
// TranslateRequestWithCodexMultiAgentV2 normalizes official Codex multi-agent
|
||||
// input before translating it to a non-Codex target protocol.
|
||||
func TranslateRequestWithCodexMultiAgentV2(ctx context.Context, headers http.Header, cfg *config.Config, from, to sdktranslator.Format, model string, payload []byte, stream bool) []byte {
|
||||
if from == sdktranslator.FormatOpenAIResponse && to != sdktranslator.FormatCodex && to != sdktranslator.FormatOpenAIResponse {
|
||||
payload = RewriteCodexMultiAgentV2Input(ctx, headers, payload, cfg)
|
||||
}
|
||||
return sdktranslator.TranslateRequest(from, to, model, payload, stream)
|
||||
}
|
||||
|
||||
// PrepareCodexMultiAgentV2Tools prepares collaboration tool definitions at the
|
||||
// Responses API boundary without changing the collaboration namespace.
|
||||
func PrepareCodexMultiAgentV2Tools(ctx context.Context, headers http.Header, payload []byte, enabled, homeEnabled bool) ([]byte, bool) {
|
||||
if !codexMultiAgentV2ClientEnabled(ctx, headers, enabled) {
|
||||
return payload, false
|
||||
}
|
||||
|
||||
toolPaths := codexSpawnAgentToolPaths(payload)
|
||||
messageToolPaths := codexCollaborationMessageToolPaths(payload)
|
||||
if len(toolPaths) == 0 && len(messageToolPaths) == 0 {
|
||||
return payload, true
|
||||
}
|
||||
if hasCodexOptimizedCollaborationConflict(payload) {
|
||||
return removeCodexCollaborationMessageEncryption(payload, messageToolPaths), true
|
||||
}
|
||||
|
||||
var models []codexSpawnAgentModel
|
||||
var formattedMarkdown string
|
||||
if len(toolPaths) > 0 {
|
||||
models, formattedMarkdown = codexSpawnAgentModelsAndMarkdownForRequest(ctx, headers, homeEnabled)
|
||||
}
|
||||
|
||||
updated := rewriteCodexCollaborationTools(payload, messageToolPaths, toolPaths, models, formattedMarkdown)
|
||||
return updated, true
|
||||
}
|
||||
|
||||
// OptimizeCodexMultiAgentV2Request rewrites an eligible spawn_agent request and
|
||||
// reports whether the collaboration namespace was renamed for upstream use.
|
||||
func OptimizeCodexMultiAgentV2Request(ctx context.Context, headers http.Header, payload []byte, cfg *config.Config) ([]byte, bool) {
|
||||
if !codexMultiAgentV2Enabled(ctx, headers, cfg) {
|
||||
return payload, false
|
||||
}
|
||||
updated := rewriteCodexAgentMessageContent(payload)
|
||||
if codexMultiAgentV2ToolsPrepared(ctx) {
|
||||
updated = removeCodexCollaborationMessageEncryption(updated, codexCollaborationMessageToolPaths(updated))
|
||||
} else {
|
||||
updated, _ = PrepareCodexMultiAgentV2Tools(ctx, headers, updated, cfg.Codex.OptimizeMultiAgentV2, cfg.Home.Enabled)
|
||||
}
|
||||
toolPaths := codexSpawnAgentToolPaths(updated)
|
||||
if len(toolPaths) == 0 || hasCodexOptimizedCollaborationConflict(updated) {
|
||||
return updated, false
|
||||
}
|
||||
return optimizeCodexCollaborationNamespace(updated, toolPaths)
|
||||
}
|
||||
|
||||
func codexMultiAgentV2Enabled(ctx context.Context, headers http.Header, cfg *config.Config) bool {
|
||||
return cfg != nil && codexMultiAgentV2ClientEnabled(ctx, headers, cfg.Codex.OptimizeMultiAgentV2)
|
||||
}
|
||||
|
||||
func codexMultiAgentV2ClientEnabled(ctx context.Context, headers http.Header, enabled bool) bool {
|
||||
return enabled && isCodexMultiAgentClient(codexClientUserAgent(ctx, headers))
|
||||
}
|
||||
|
||||
func codexMultiAgentV2ToolsPrepared(ctx context.Context) bool {
|
||||
if ctx == nil {
|
||||
return false
|
||||
}
|
||||
ginCtx, ok := ctx.Value("gin").(*gin.Context)
|
||||
if !ok || ginCtx == nil {
|
||||
return false
|
||||
}
|
||||
prepared, ok := ginCtx.Get(CodexMultiAgentV2ToolsPreparedContextKey)
|
||||
isPrepared, _ := prepared.(bool)
|
||||
return ok && isPrepared
|
||||
}
|
||||
|
||||
func codexClientUserAgent(ctx context.Context, headers http.Header) string {
|
||||
if ctx != nil {
|
||||
if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil && ginCtx.Request != nil {
|
||||
return headerValueCaseInsensitive(ginCtx.Request.Header, "User-Agent")
|
||||
}
|
||||
}
|
||||
return headerValueCaseInsensitive(headers, "User-Agent")
|
||||
}
|
||||
|
||||
func headerValueCaseInsensitive(headers http.Header, name string) string {
|
||||
if headers == nil {
|
||||
return ""
|
||||
}
|
||||
if value := strings.TrimSpace(headers.Get(name)); value != "" {
|
||||
return value
|
||||
}
|
||||
for key, values := range headers {
|
||||
if !strings.EqualFold(key, name) {
|
||||
continue
|
||||
}
|
||||
for _, value := range values {
|
||||
if value = strings.TrimSpace(value); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsCodexClientUserAgent reports whether a request uses an official Codex client identity.
|
||||
func IsCodexClientUserAgent(userAgent string) bool {
|
||||
userAgent = strings.TrimSpace(userAgent)
|
||||
return strings.HasPrefix(userAgent, "Codex Desktop/") ||
|
||||
strings.HasPrefix(userAgent, "codex-tui/") ||
|
||||
userAgent == "codex_cli_rs" ||
|
||||
strings.HasPrefix(userAgent, "codex_cli_rs/")
|
||||
}
|
||||
|
||||
func isCodexMultiAgentClient(userAgent string) bool {
|
||||
return IsCodexClientUserAgent(userAgent)
|
||||
}
|
||||
|
||||
var (
|
||||
codexCatalogTemplatesMu sync.RWMutex
|
||||
codexCatalogTemplatesLoaded bool
|
||||
codexCatalogTemplatesRevision uint64
|
||||
codexCatalogTemplates map[string]map[string]any
|
||||
codexCatalogDefaultTemplate map[string]any
|
||||
|
||||
codexSpawnAgentCacheMu sync.RWMutex
|
||||
codexSpawnAgentCacheRevision uint64
|
||||
codexSpawnAgentCacheGeneration uint64
|
||||
codexSpawnAgentCachedModels []codexSpawnAgentModel
|
||||
codexSpawnAgentCachedMarkdown string
|
||||
)
|
||||
|
||||
func loadCodexCatalogTemplates() (map[string]map[string]any, map[string]any, uint64, error) {
|
||||
currentRevision := registry.GetCodexClientModelsRevision()
|
||||
|
||||
codexCatalogTemplatesMu.RLock()
|
||||
if codexCatalogTemplatesLoaded && codexCatalogTemplatesRevision == currentRevision {
|
||||
templates := codexCatalogTemplates
|
||||
defaultTemplate := codexCatalogDefaultTemplate
|
||||
codexCatalogTemplatesMu.RUnlock()
|
||||
return templates, defaultTemplate, currentRevision, nil
|
||||
}
|
||||
codexCatalogTemplatesMu.RUnlock()
|
||||
|
||||
codexCatalogTemplatesMu.Lock()
|
||||
defer codexCatalogTemplatesMu.Unlock()
|
||||
if codexCatalogTemplatesLoaded && codexCatalogTemplatesRevision == currentRevision {
|
||||
return codexCatalogTemplates, codexCatalogDefaultTemplate, currentRevision, nil
|
||||
}
|
||||
|
||||
raw, revision := registry.GetCodexClientModelsSnapshot()
|
||||
|
||||
var catalog codexClientModelsCatalog
|
||||
errUnmarshal := json.Unmarshal(raw, &catalog)
|
||||
if errUnmarshal != nil || len(catalog.Models) == 0 {
|
||||
codexCatalogTemplatesLoaded = true
|
||||
codexCatalogTemplatesRevision = revision
|
||||
codexCatalogTemplates = nil
|
||||
codexCatalogDefaultTemplate = nil
|
||||
return nil, nil, revision, errUnmarshal
|
||||
}
|
||||
|
||||
templates := make(map[string]map[string]any, len(catalog.Models))
|
||||
var defaultTemplate map[string]any
|
||||
for _, model := range catalog.Models {
|
||||
modelID := mapString(model, "slug")
|
||||
if modelID == "" {
|
||||
continue
|
||||
}
|
||||
templates[modelID] = model
|
||||
if modelID == "gpt-5.5" {
|
||||
defaultTemplate = model
|
||||
}
|
||||
}
|
||||
|
||||
codexCatalogTemplatesLoaded = true
|
||||
codexCatalogTemplatesRevision = revision
|
||||
codexCatalogTemplates = templates
|
||||
codexCatalogDefaultTemplate = defaultTemplate
|
||||
return templates, defaultTemplate, revision, nil
|
||||
}
|
||||
|
||||
func codexSpawnAgentModelsAndMarkdownForRequest(ctx context.Context, headers http.Header, homeEnabled bool) ([]codexSpawnAgentModel, string) {
|
||||
if homeEnabled {
|
||||
availableModels := codexHomeAvailableModels(ctx, headers)
|
||||
templates, defaultTemplate, _, errLoad := loadCodexCatalogTemplates()
|
||||
if errLoad != nil || defaultTemplate == nil {
|
||||
return nil, ""
|
||||
}
|
||||
models := codexSpawnAgentModelsFromTemplates(availableModels, templates, defaultTemplate, func(modelID string) *registry.ModelInfo {
|
||||
return registry.LookupModelInfo(modelID)
|
||||
})
|
||||
formatted := formatCodexSpawnAgentModels(models)
|
||||
return models, formatted
|
||||
}
|
||||
|
||||
currentRevision := registry.GetCodexClientModelsRevision()
|
||||
currentGeneration := registry.GetGlobalRegistry().GetGeneration()
|
||||
|
||||
codexSpawnAgentCacheMu.RLock()
|
||||
if codexSpawnAgentCachedModels != nil && codexSpawnAgentCacheRevision == currentRevision && codexSpawnAgentCacheGeneration == currentGeneration {
|
||||
models := codexSpawnAgentCachedModels
|
||||
markdown := codexSpawnAgentCachedMarkdown
|
||||
codexSpawnAgentCacheMu.RUnlock()
|
||||
return models, markdown
|
||||
}
|
||||
codexSpawnAgentCacheMu.RUnlock()
|
||||
|
||||
templates, defaultTemplate, _, errLoad := loadCodexCatalogTemplates()
|
||||
if errLoad != nil || defaultTemplate == nil {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
availableModels := registry.GetGlobalRegistry().GetAvailableModels("openai")
|
||||
lookup := func(modelID string) *registry.ModelInfo {
|
||||
return registry.LookupModelInfo(modelID)
|
||||
}
|
||||
models := codexSpawnAgentModelsFromTemplates(availableModels, templates, defaultTemplate, lookup)
|
||||
formatted := formatCodexSpawnAgentModels(models)
|
||||
|
||||
codexSpawnAgentCacheMu.Lock()
|
||||
if currentRevision == registry.GetCodexClientModelsRevision() && currentGeneration == registry.GetGlobalRegistry().GetGeneration() {
|
||||
codexSpawnAgentCacheRevision = currentRevision
|
||||
codexSpawnAgentCacheGeneration = currentGeneration
|
||||
codexSpawnAgentCachedModels = models
|
||||
codexSpawnAgentCachedMarkdown = formatted
|
||||
}
|
||||
codexSpawnAgentCacheMu.Unlock()
|
||||
|
||||
return models, formatted
|
||||
}
|
||||
|
||||
func codexSpawnAgentModelsForRequest(ctx context.Context, headers http.Header, homeEnabled bool) []codexSpawnAgentModel {
|
||||
models, _ := codexSpawnAgentModelsAndMarkdownForRequest(ctx, headers, homeEnabled)
|
||||
return models
|
||||
}
|
||||
|
||||
func formatCodexSpawnAgentModelsForRequest(ctx context.Context, headers http.Header, homeEnabled bool) string {
|
||||
_, formatted := codexSpawnAgentModelsAndMarkdownForRequest(ctx, headers, homeEnabled)
|
||||
return formatted
|
||||
}
|
||||
|
||||
func codexHomeAvailableModels(ctx context.Context, headers http.Header) []map[string]any {
|
||||
client := home.Current()
|
||||
if client == nil {
|
||||
return nil
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
requestHeaders := headers
|
||||
if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil && ginCtx.Request != nil {
|
||||
requestHeaders = ginCtx.Request.Header
|
||||
}
|
||||
query := make(url.Values)
|
||||
query.Set("client_version", "")
|
||||
raw, errGet := client.GetModels(ctx, requestHeaders, query)
|
||||
if errGet != nil {
|
||||
return nil
|
||||
}
|
||||
return decodeCodexHomeAvailableModels(raw)
|
||||
}
|
||||
|
||||
func decodeCodexHomeAvailableModels(raw []byte) []map[string]any {
|
||||
var sections map[string][]map[string]any
|
||||
if err := json.Unmarshal(raw, §ions); err != nil || len(sections) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{})
|
||||
models := make([]map[string]any, 0, 256)
|
||||
for _, sectionModels := range sections {
|
||||
for _, model := range sectionModels {
|
||||
modelID := mapString(model, "id")
|
||||
if modelID == "" {
|
||||
modelID = strings.TrimPrefix(mapString(model, "name"), "models/")
|
||||
}
|
||||
if modelID == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[modelID]; exists {
|
||||
continue
|
||||
}
|
||||
seen[modelID] = struct{}{}
|
||||
|
||||
displayName := mapString(model, "display_name")
|
||||
if displayName == "" {
|
||||
displayName = mapString(model, "displayName")
|
||||
}
|
||||
entry := map[string]any{"id": modelID}
|
||||
if displayName != "" {
|
||||
entry["display_name"] = displayName
|
||||
entry["description"] = displayName
|
||||
}
|
||||
models = append(models, entry)
|
||||
}
|
||||
}
|
||||
sort.Slice(models, func(i, j int) bool {
|
||||
return mapString(models[i], "id") < mapString(models[j], "id")
|
||||
})
|
||||
return models
|
||||
}
|
||||
|
||||
func codexSpawnAgentModelsFromSources(availableModels []map[string]any, catalogJSON []byte, lookupModel func(string) *registry.ModelInfo) []codexSpawnAgentModel {
|
||||
var catalog codexClientModelsCatalog
|
||||
if err := json.Unmarshal(catalogJSON, &catalog); err != nil || len(catalog.Models) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
templates := make(map[string]map[string]any, len(catalog.Models))
|
||||
var defaultTemplate map[string]any
|
||||
for _, model := range catalog.Models {
|
||||
modelID := mapString(model, "slug")
|
||||
if modelID == "" {
|
||||
continue
|
||||
}
|
||||
templates[modelID] = model
|
||||
if modelID == "gpt-5.5" {
|
||||
defaultTemplate = model
|
||||
}
|
||||
}
|
||||
if defaultTemplate == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return codexSpawnAgentModelsFromTemplates(availableModels, templates, defaultTemplate, lookupModel)
|
||||
}
|
||||
|
||||
func codexSpawnAgentModelsFromTemplates(availableModels []map[string]any, templates map[string]map[string]any, defaultTemplate map[string]any, lookupModel func(string) *registry.ModelInfo) []codexSpawnAgentModel {
|
||||
if defaultTemplate == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(availableModels))
|
||||
templateModels := make([]codexSpawnAgentModel, 0, len(availableModels))
|
||||
synthesizedModels := make([]codexSpawnAgentModel, 0, len(availableModels))
|
||||
for _, availableModel := range availableModels {
|
||||
modelID := mapString(availableModel, "id")
|
||||
if modelID == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[modelID]; exists {
|
||||
continue
|
||||
}
|
||||
seen[modelID] = struct{}{}
|
||||
|
||||
if template, ok := templates[modelID]; ok {
|
||||
templateModels = append(templateModels, codexSpawnAgentModelFromMetadata(modelID, template))
|
||||
continue
|
||||
}
|
||||
|
||||
profile := codexSpawnAgentModelFromMetadata(modelID, defaultTemplate)
|
||||
profile.id = modelID
|
||||
profile.description = mapString(availableModel, "description")
|
||||
profile.displayName = mapString(availableModel, "display_name")
|
||||
if profile.displayName == "" {
|
||||
profile.displayName = modelID
|
||||
}
|
||||
if lookupModel != nil {
|
||||
if info := lookupModel(modelID); info != nil {
|
||||
if strings.TrimSpace(info.Description) != "" {
|
||||
profile.description = strings.TrimSpace(info.Description)
|
||||
}
|
||||
applyCodexSpawnAgentThinking(&profile, info.Thinking)
|
||||
}
|
||||
}
|
||||
if profile.description == "" {
|
||||
profile.description = modelID
|
||||
}
|
||||
profile.serviceTiers = nil
|
||||
synthesizedModels = append(synthesizedModels, profile)
|
||||
}
|
||||
|
||||
sort.SliceStable(templateModels, func(i, j int) bool {
|
||||
if templateModels[i].priority == templateModels[j].priority {
|
||||
return templateModels[i].id < templateModels[j].id
|
||||
}
|
||||
return templateModels[i].priority < templateModels[j].priority
|
||||
})
|
||||
sort.SliceStable(synthesizedModels, func(i, j int) bool {
|
||||
left := strings.ToLower(synthesizedModels[i].displayName)
|
||||
right := strings.ToLower(synthesizedModels[j].displayName)
|
||||
if left == right {
|
||||
return synthesizedModels[i].id < synthesizedModels[j].id
|
||||
}
|
||||
return left < right
|
||||
})
|
||||
return append(templateModels, synthesizedModels...)
|
||||
}
|
||||
|
||||
func codexSpawnAgentModelFromMetadata(modelID string, metadata map[string]any) codexSpawnAgentModel {
|
||||
profile := codexSpawnAgentModel{
|
||||
id: modelID,
|
||||
description: mapString(metadata, "description"),
|
||||
displayName: mapString(metadata, "display_name"),
|
||||
priority: mapInt(metadata, "priority"),
|
||||
}
|
||||
profile.reasoningEfforts, profile.defaultReasoningEffort = codexReasoningMetadata(metadata)
|
||||
profile.serviceTiers = codexServiceTierIDs(metadata)
|
||||
return profile
|
||||
}
|
||||
|
||||
func applyCodexSpawnAgentThinking(profile *codexSpawnAgentModel, thinking *registry.ThinkingSupport) {
|
||||
if profile == nil || thinking == nil || len(thinking.Levels) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
efforts := make([]string, 0, len(thinking.Levels))
|
||||
defaultEffort := ""
|
||||
firstEffort := ""
|
||||
for _, rawEffort := range thinking.Levels {
|
||||
effort := normalizeCodexReasoningEffort(rawEffort)
|
||||
if effort == "" {
|
||||
continue
|
||||
}
|
||||
if firstEffort == "" {
|
||||
firstEffort = effort
|
||||
}
|
||||
if (defaultEffort == "" && effort != "none") || effort == "medium" {
|
||||
defaultEffort = effort
|
||||
}
|
||||
efforts = append(efforts, effort)
|
||||
}
|
||||
if len(efforts) == 0 {
|
||||
return
|
||||
}
|
||||
if defaultEffort == "" {
|
||||
defaultEffort = firstEffort
|
||||
}
|
||||
profile.reasoningEfforts = efforts
|
||||
profile.defaultReasoningEffort = defaultEffort
|
||||
}
|
||||
|
||||
func codexReasoningMetadata(metadata map[string]any) ([]string, string) {
|
||||
rawLevels, _ := metadata["supported_reasoning_levels"].([]any)
|
||||
efforts := make([]string, 0, len(rawLevels))
|
||||
allowed := make(map[string]struct{}, len(rawLevels))
|
||||
for _, rawLevel := range rawLevels {
|
||||
level, _ := rawLevel.(map[string]any)
|
||||
effort := normalizeCodexReasoningEffort(mapString(level, "effort"))
|
||||
if effort == "" {
|
||||
continue
|
||||
}
|
||||
efforts = append(efforts, effort)
|
||||
allowed[effort] = struct{}{}
|
||||
}
|
||||
if len(efforts) == 0 {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
defaultEffort := normalizeCodexReasoningEffort(mapString(metadata, "default_reasoning_level"))
|
||||
if _, ok := allowed[defaultEffort]; !ok {
|
||||
defaultEffort = efforts[0]
|
||||
}
|
||||
return efforts, defaultEffort
|
||||
}
|
||||
|
||||
func normalizeCodexReasoningEffort(effort string) string {
|
||||
effort = strings.ToLower(strings.TrimSpace(effort))
|
||||
switch effort {
|
||||
case "none", "low", "medium", "high", "xhigh", "max", "ultra":
|
||||
return effort
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func codexServiceTierIDs(metadata map[string]any) []string {
|
||||
rawTiers, _ := metadata["service_tiers"].([]any)
|
||||
tiers := make([]string, 0, len(rawTiers))
|
||||
seen := make(map[string]struct{}, len(rawTiers))
|
||||
for _, rawTier := range rawTiers {
|
||||
tier, _ := rawTier.(map[string]any)
|
||||
tierID := mapString(tier, "id")
|
||||
if tierID == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[tierID]; exists {
|
||||
continue
|
||||
}
|
||||
seen[tierID] = struct{}{}
|
||||
tiers = append(tiers, tierID)
|
||||
}
|
||||
return tiers
|
||||
}
|
||||
|
||||
func mapString(values map[string]any, key string) string {
|
||||
if values == nil {
|
||||
return ""
|
||||
}
|
||||
value, _ := values[key].(string)
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
|
||||
func mapInt(values map[string]any, key string) int {
|
||||
if values == nil {
|
||||
return 0
|
||||
}
|
||||
switch value := values[key].(type) {
|
||||
case int:
|
||||
return value
|
||||
case int64:
|
||||
return int(value)
|
||||
case float64:
|
||||
return int(value)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func rewriteCodexSpawnAgentDescription(payload []byte, models []codexSpawnAgentModel) []byte {
|
||||
return rewriteCodexSpawnAgentTools(payload, codexSpawnAgentToolPaths(payload), models)
|
||||
}
|
||||
|
||||
func rewriteCodexSpawnAgentTools(payload []byte, toolPaths []string, models []codexSpawnAgentModel) []byte {
|
||||
return rewriteCodexCollaborationTools(payload, toolPaths, toolPaths, models, "")
|
||||
}
|
||||
|
||||
func rewriteCodexCollaborationTools(payload []byte, messageToolPaths, spawnAgentToolPaths []string, models []codexSpawnAgentModel, modelList string) []byte {
|
||||
if len(messageToolPaths) == 0 && len(spawnAgentToolPaths) == 0 {
|
||||
return payload
|
||||
}
|
||||
if modelList == "" && len(models) > 0 {
|
||||
modelList = formatCodexSpawnAgentModels(models)
|
||||
}
|
||||
updated := payload
|
||||
for _, toolPath := range spawnAgentToolPaths {
|
||||
descriptionPath := toolPath + ".description"
|
||||
description := gjson.GetBytes(updated, descriptionPath)
|
||||
if description.Type == gjson.String && modelList != "" {
|
||||
rewritten := replaceCodexSpawnAgentModels(description.String(), modelList)
|
||||
if rewritten != description.String() {
|
||||
var errSet error
|
||||
updated, errSet = sjson.SetBytes(updated, descriptionPath, rewritten)
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, toolPath := range messageToolPaths {
|
||||
encryptedPath := toolPath + ".parameters.properties.message.encrypted"
|
||||
if gjson.GetBytes(updated, encryptedPath).Exists() {
|
||||
var errDelete error
|
||||
updated, errDelete = sjson.DeleteBytes(updated, encryptedPath)
|
||||
if errDelete != nil {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
// HasCodexMultiAgentV2NamespaceConflict reports whether the request defines
|
||||
// the reserved optimized namespace, which must remain untouched.
|
||||
func HasCodexMultiAgentV2NamespaceConflict(payload []byte) bool {
|
||||
return hasCodexOptimizedCollaborationConflict(payload)
|
||||
}
|
||||
|
||||
func hasCodexOptimizedCollaborationConflict(payload []byte) bool {
|
||||
if codexToolsHaveOptimizedCollaborationConflict(gjson.GetBytes(payload, "tools")) {
|
||||
return true
|
||||
}
|
||||
input := gjson.GetBytes(payload, "input")
|
||||
if !input.IsArray() {
|
||||
return false
|
||||
}
|
||||
for _, item := range input.Array() {
|
||||
if strings.TrimSpace(item.Get("type").String()) == "additional_tools" && codexToolsHaveOptimizedCollaborationConflict(item.Get("tools")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func codexToolsHaveOptimizedCollaborationConflict(tools gjson.Result) bool {
|
||||
if !tools.IsArray() {
|
||||
return false
|
||||
}
|
||||
for _, tool := range tools.Array() {
|
||||
name := strings.TrimSpace(tool.Get("name").String())
|
||||
if name == codexOptimizedCollaborationNamespace || strings.HasPrefix(name, codexOptimizedCollaborationNamePrefix) {
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(tool.Get("type").String()) == "namespace" && codexToolsHaveOptimizedCollaborationConflict(tool.Get("tools")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func optimizeCodexCollaborationNamespace(payload []byte, toolPaths []string) ([]byte, bool) {
|
||||
updated := payload
|
||||
optimized := false
|
||||
for _, toolPath := range toolPaths {
|
||||
separatorIndex := strings.LastIndex(toolPath, ".tools.")
|
||||
if separatorIndex < 0 {
|
||||
continue
|
||||
}
|
||||
namespacePath := toolPath[:separatorIndex]
|
||||
namespace := gjson.GetBytes(updated, namespacePath)
|
||||
if strings.TrimSpace(namespace.Get("type").String()) != "namespace" || strings.TrimSpace(namespace.Get("name").String()) != codexCollaborationNamespace {
|
||||
continue
|
||||
}
|
||||
var errSet error
|
||||
updated, errSet = sjson.SetBytes(updated, namespacePath+".name", codexOptimizedCollaborationNamespace)
|
||||
if errSet != nil {
|
||||
return payload, false
|
||||
}
|
||||
optimized = true
|
||||
}
|
||||
return updated, optimized
|
||||
}
|
||||
|
||||
// RestoreCodexMultiAgentV2Response restores optimized collaboration namespace
|
||||
// values before an upstream response is translated and returned to the client.
|
||||
func RestoreCodexMultiAgentV2Response(payload []byte, optimized bool) []byte {
|
||||
if !optimized || len(payload) == 0 || !gjson.ValidBytes(payload) {
|
||||
return payload
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(payload))
|
||||
decoder.UseNumber()
|
||||
var value any
|
||||
if errDecode := decoder.Decode(&value); errDecode != nil {
|
||||
return payload
|
||||
}
|
||||
if !restoreCodexCollaborationValue(value) {
|
||||
return payload
|
||||
}
|
||||
restored, errMarshal := json.Marshal(value)
|
||||
if errMarshal != nil {
|
||||
return payload
|
||||
}
|
||||
return restored
|
||||
}
|
||||
|
||||
func restoreCodexCollaborationValue(value any) bool {
|
||||
changed := false
|
||||
switch typed := value.(type) {
|
||||
case []any:
|
||||
for _, item := range typed {
|
||||
if restoreCodexCollaborationValue(item) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
case map[string]any:
|
||||
itemType := strings.TrimSpace(mapString(typed, "type"))
|
||||
isToolCall := itemType == "function_call" || itemType == "custom_tool_call"
|
||||
if isToolCall {
|
||||
if namespace, ok := typed["namespace"].(string); ok && namespace == codexOptimizedCollaborationNamespace {
|
||||
typed["namespace"] = codexCollaborationNamespace
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if name, ok := typed["name"].(string); ok {
|
||||
switch {
|
||||
case name == codexOptimizedCollaborationNamespace && itemType == "namespace":
|
||||
typed["name"] = codexCollaborationNamespace
|
||||
changed = true
|
||||
case isToolCall && strings.HasPrefix(name, codexOptimizedCollaborationNamePrefix):
|
||||
typed["name"] = codexCollaborationNamespace + "__" + strings.TrimPrefix(name, codexOptimizedCollaborationNamePrefix)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
for key, child := range typed {
|
||||
if key == "arguments" || key == "input" || key == "output" && (itemType == "function_call_output" || itemType == "custom_tool_call_output") {
|
||||
continue
|
||||
}
|
||||
if restoreCodexCollaborationValue(child) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func rewriteCodexAgentMessageInput(payload []byte) []byte {
|
||||
input := gjson.GetBytes(payload, "input")
|
||||
if !input.IsArray() {
|
||||
return payload
|
||||
}
|
||||
|
||||
updated := rewriteCodexAgentMessageContent(payload)
|
||||
for itemIndex, item := range input.Array() {
|
||||
if strings.TrimSpace(item.Get("type").String()) != "agent_message" {
|
||||
continue
|
||||
}
|
||||
itemPath := fmt.Sprintf("input.%d", itemIndex)
|
||||
var errSet error
|
||||
updated, errSet = sjson.SetBytes(updated, itemPath+".role", "user")
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
updated, errSet = sjson.SetBytes(updated, itemPath+".type", "message")
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func rewriteCodexAgentMessageContent(payload []byte) []byte {
|
||||
input := gjson.GetBytes(payload, "input")
|
||||
if !input.IsArray() {
|
||||
return payload
|
||||
}
|
||||
|
||||
updated := payload
|
||||
for itemIndex, item := range input.Array() {
|
||||
if strings.TrimSpace(item.Get("type").String()) != "agent_message" {
|
||||
continue
|
||||
}
|
||||
content := item.Get("content")
|
||||
if !content.IsArray() {
|
||||
continue
|
||||
}
|
||||
for partIndex, part := range content.Array() {
|
||||
if strings.TrimSpace(part.Get("type").String()) != "encrypted_content" {
|
||||
continue
|
||||
}
|
||||
encryptedContent := part.Get("encrypted_content")
|
||||
if encryptedContent.Type != gjson.String {
|
||||
continue
|
||||
}
|
||||
partPath := fmt.Sprintf("input.%d.content.%d", itemIndex, partIndex)
|
||||
var errSet error
|
||||
updated, errSet = sjson.SetBytes(updated, partPath+".type", "input_text")
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
updated, errSet = sjson.SetBytes(updated, partPath+".text", encryptedContent.String())
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
updated, errSet = sjson.DeleteBytes(updated, partPath+".encrypted_content")
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func codexSpawnAgentToolPaths(payload []byte) []string {
|
||||
return codexToolPathsByNames(payload, map[string]struct{}{"spawn_agent": {}})
|
||||
}
|
||||
|
||||
// codexCollaborationMessageToolPaths discovers function tools named
|
||||
// spawn_agent, send_message, or followup_task inside top-level tools arrays and
|
||||
// input[].additional_tools arrays, including nested namespace tools.
|
||||
func codexCollaborationMessageToolPaths(payload []byte) []string {
|
||||
return codexToolPathsByNames(payload, codexCollaborationMessageTools)
|
||||
}
|
||||
|
||||
func codexToolPathsByNames(payload []byte, names map[string]struct{}) []string {
|
||||
paths := make([]string, 0, len(names))
|
||||
collectCodexToolPathsByNames(gjson.GetBytes(payload, "tools"), "tools", &paths, names)
|
||||
|
||||
input := gjson.GetBytes(payload, "input")
|
||||
if input.IsArray() {
|
||||
for index, item := range input.Array() {
|
||||
if strings.TrimSpace(item.Get("type").String()) != "additional_tools" {
|
||||
continue
|
||||
}
|
||||
collectCodexToolPathsByNames(item.Get("tools"), fmt.Sprintf("input.%d.tools", index), &paths, names)
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func collectCodexToolPathsByNames(tools gjson.Result, path string, paths *[]string, names map[string]struct{}) {
|
||||
if !tools.IsArray() {
|
||||
return
|
||||
}
|
||||
for index, tool := range tools.Array() {
|
||||
toolPath := fmt.Sprintf("%s.%d", path, index)
|
||||
toolType := strings.TrimSpace(tool.Get("type").String())
|
||||
if toolType == "function" {
|
||||
if _, ok := names[strings.TrimSpace(tool.Get("name").String())]; ok {
|
||||
*paths = append(*paths, toolPath)
|
||||
}
|
||||
}
|
||||
if toolType == "namespace" {
|
||||
collectCodexToolPathsByNames(tool.Get("tools"), toolPath+".tools", paths, names)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeCodexCollaborationMessageEncryption deletes the
|
||||
// parameters.properties.message.encrypted field from each discovered
|
||||
// collaboration message tool so the proxy can read the plaintext message.
|
||||
func removeCodexCollaborationMessageEncryption(payload []byte, toolPaths []string) []byte {
|
||||
updated := payload
|
||||
for _, toolPath := range toolPaths {
|
||||
encryptedPath := toolPath + ".parameters.properties.message.encrypted"
|
||||
if !gjson.GetBytes(updated, encryptedPath).Exists() {
|
||||
continue
|
||||
}
|
||||
var errDelete error
|
||||
updated, errDelete = sjson.DeleteBytes(updated, encryptedPath)
|
||||
if errDelete != nil {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func formatCodexSpawnAgentModels(models []codexSpawnAgentModel) string {
|
||||
var modelList strings.Builder
|
||||
for _, model := range models {
|
||||
modelID := strings.Join(strings.Fields(model.id), " ")
|
||||
if modelID == "" {
|
||||
continue
|
||||
}
|
||||
modelList.WriteString("- ")
|
||||
modelList.WriteString(markdownCode(modelID))
|
||||
modelList.WriteString(": ")
|
||||
hasDetails := false
|
||||
if description := strings.Join(strings.Fields(model.description), " "); description != "" {
|
||||
writeSentence(&modelList, description)
|
||||
hasDetails = true
|
||||
}
|
||||
if len(model.reasoningEfforts) > 0 {
|
||||
if hasDetails {
|
||||
modelList.WriteByte(' ')
|
||||
}
|
||||
modelList.WriteString("Reasoning efforts: ")
|
||||
for index, effort := range model.reasoningEfforts {
|
||||
if index > 0 {
|
||||
modelList.WriteString(", ")
|
||||
}
|
||||
modelList.WriteString(effort)
|
||||
if effort == model.defaultReasoningEffort {
|
||||
modelList.WriteString(" (default)")
|
||||
}
|
||||
}
|
||||
modelList.WriteByte('.')
|
||||
hasDetails = true
|
||||
}
|
||||
if len(model.serviceTiers) > 0 {
|
||||
if hasDetails {
|
||||
modelList.WriteByte(' ')
|
||||
}
|
||||
modelList.WriteString("Service tiers: ")
|
||||
modelList.WriteString(strings.Join(model.serviceTiers, ", "))
|
||||
modelList.WriteByte('.')
|
||||
}
|
||||
modelList.WriteByte('\n')
|
||||
}
|
||||
return strings.TrimSuffix(modelList.String(), "\n")
|
||||
}
|
||||
|
||||
func markdownCode(value string) string {
|
||||
if strings.Contains(value, "`") {
|
||||
return "`` " + value + " ``"
|
||||
}
|
||||
return "`" + value + "`"
|
||||
}
|
||||
|
||||
func writeSentence(builder *strings.Builder, value string) {
|
||||
builder.WriteString(value)
|
||||
if !strings.ContainsAny(value[len(value)-1:], ".!?") {
|
||||
builder.WriteByte('.')
|
||||
}
|
||||
}
|
||||
|
||||
func replaceCodexSpawnAgentModels(description, modelList string) string {
|
||||
if modelList == "" {
|
||||
return description
|
||||
}
|
||||
|
||||
cleaned, headingIndent := removeCodexSpawnAgentModelSections(description)
|
||||
section := headingIndent + codexSpawnAgentModelsHeading + "\n" + modelList + "\n"
|
||||
markerIndex := strings.Index(cleaned, codexSpawnAgentDescriptionMarker)
|
||||
if markerIndex >= 0 {
|
||||
markerLineStart := strings.LastIndex(cleaned[:markerIndex], "\n") + 1
|
||||
return cleaned[:markerLineStart] + section + cleaned[markerLineStart:]
|
||||
}
|
||||
separator := ""
|
||||
if cleaned != "" && !strings.HasSuffix(cleaned, "\n") {
|
||||
separator = "\n\n"
|
||||
}
|
||||
return cleaned + separator + strings.TrimSuffix(section, "\n")
|
||||
}
|
||||
|
||||
func removeCodexSpawnAgentModelSections(description string) (string, string) {
|
||||
if !strings.Contains(description, codexSpawnAgentModelsHeading) {
|
||||
return description, ""
|
||||
}
|
||||
lines := strings.SplitAfter(description, "\n")
|
||||
var cleaned strings.Builder
|
||||
headingIndent := ""
|
||||
for index := 0; index < len(lines); {
|
||||
line := lines[index]
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if trimmedLine != codexSpawnAgentModelsHeading {
|
||||
cleaned.WriteString(line)
|
||||
index++
|
||||
continue
|
||||
}
|
||||
|
||||
if headingIndent == "" {
|
||||
headingIndex := strings.Index(line, codexSpawnAgentModelsHeading)
|
||||
if headingIndex > 0 {
|
||||
headingIndent = line[:headingIndex]
|
||||
}
|
||||
}
|
||||
index++
|
||||
for index < len(lines) && strings.HasPrefix(strings.TrimSpace(lines[index]), "- ") {
|
||||
index++
|
||||
}
|
||||
}
|
||||
return cleaned.String(), headingIndent
|
||||
}
|
||||
Loading…
Reference in a new issue