(ctx context.Context, sess *session.Session, agentFilename, currentAgent string, rc *config.RuntimeConfig)
| 913 | } |
| 914 | |
| 915 | func (sm *SessionManager) runtimeForSession(ctx context.Context, sess *session.Session, agentFilename, currentAgent string, rc *config.RuntimeConfig) (_ runtime.Runtime, _ *sessiontitle.Generator, err error) { |
| 916 | // Caller (RunSession) holds sm.mux and has already verified that no |
| 917 | // active runtime exists for this session. This function is purely a |
| 918 | // constructor: it must not touch sm.runtimeSessions, otherwise it would |
| 919 | // briefly publish a half-initialised activeRuntimes (e.g. without the |
| 920 | // cancel func) that other goroutines could observe. |
| 921 | // |
| 922 | // Every call is a cold-path construction (caller short-circuits |
| 923 | // cached hits), so a span here attributes per-request first-use |
| 924 | // latency (team load + runtime construction) without adding noise |
| 925 | // on warm paths. |
| 926 | ctx, span := otel.Tracer("github.com/docker/docker-agent/pkg/server").Start( |
| 927 | ctx, "session.runtime_init", |
| 928 | trace.WithSpanKind(trace.SpanKindInternal), |
| 929 | trace.WithAttributes(attribute.String("gen_ai.conversation.id", sess.ID)), |
| 930 | ) |
| 931 | defer func() { |
| 932 | if err != nil { |
| 933 | span.RecordError(err) |
| 934 | span.SetStatus(codes.Error, err.Error()) |
| 935 | } |
| 936 | span.End() |
| 937 | }() |
| 938 | |
| 939 | loadResult, err := sm.loadTeamWithConfig(ctx, agentFilename, rc) |
| 940 | if err != nil { |
| 941 | return nil, nil, err |
| 942 | } |
| 943 | t := loadResult.Team |
| 944 | |
| 945 | // Resolve the team's default agent when no specific agent was requested. |
| 946 | agt, err := t.AgentOrDefault(currentAgent) |
| 947 | if err != nil { |
| 948 | return nil, nil, err |
| 949 | } |
| 950 | currentAgent = agt.Name() |
| 951 | sess.MaxIterations = agt.MaxIterations() |
| 952 | sess.MaxConsecutiveToolCalls = agt.MaxConsecutiveToolCalls() |
| 953 | sess.MaxOldToolCallTokens = agt.MaxOldToolCallTokens() |
| 954 | |
| 955 | modelSwitcherCfg := &runtime.ModelSwitcherConfig{ |
| 956 | Models: loadResult.Models, |
| 957 | Providers: loadResult.Providers, |
| 958 | ModelsGateway: rc.ModelsGateway, |
| 959 | EnvProvider: rc.EnvProvider(), |
| 960 | ProviderRegistry: loadResult.ProviderRegistry, |
| 961 | AgentDefaultModels: loadResult.AgentDefaultModels, |
| 962 | } |
| 963 | // Reuse the models.dev store the team loader already warmed so the |
| 964 | // /api/sessions/:id/models picker doesn't re-pay the cold catalog parse. |
| 965 | if store, storeErr := rc.ModelsDevStore(); storeErr == nil { |
| 966 | modelSwitcherCfg.ModelsStore = store |
| 967 | } else { |
| 968 | slog.WarnContext(ctx, "Failed to obtain shared models.dev store; runtime will use its own", "error", storeErr) |
| 969 | } |
| 970 | |
| 971 | opts := []runtime.Opt{ |
| 972 | runtime.WithCurrentAgent(currentAgent), |
no test coverage detected