mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Stacked on #27703. Provider option conversion, reasoning effort injection, and file part acceptance each recomputed the OpenAI wire format from `(provider, modelID, override)`. They now read it from `chatprovider.Model`, so a decision cannot drift from the client it was built for. `ProviderOptionsFromChatConfig` takes a `Transport`, `ApplyReasoningEffort` takes a `Model`, and `AcceptsFilePartMediaType` becomes a `Model` method. `UsesResponsesAPI` and `UsesResponsesOptions` are deleted. The override extraction is unexported and reachable only from `ModelFromConfig`, which now takes the model's `ChatModelOpenAIConfig` directly, removing the six scattered extractions at call sites. That also resolves the computer-use mismatch. The computer-use model is a hardcoded default with no config row of its own: its client was built without an override while request preparation applied the chat model's. Preparation now reads the computer-use model's own transport, so the two agree without one model's client settings following a different model. Passing the chat model's `openai_config` into the computer-use client would have made them agree on the wrong value. `TestModelTransportConsumersAgree` pins the invariant in one test: the HTTP path the client actually hits, the concrete provider option struct type, the type created by reasoning effort, and text/image file acceptance. > Mux prepared this PR on Mike's behalf.
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
package chatd
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/google/uuid"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/x/chatd/chatprovider"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
type modelClientRequest struct {
|
|
Chat database.Chat
|
|
ModelName string
|
|
UserAgent string
|
|
ExtraHeaders map[string]string
|
|
// ConfigOptions holds the model config row's Options JSONB; empty for
|
|
// paths without a config row.
|
|
ConfigOptions json.RawMessage
|
|
}
|
|
|
|
type modelBuildOptions struct {
|
|
ActiveAPIKeyID string
|
|
RecordHTTP bool
|
|
}
|
|
|
|
func (p *Server) enabledAIProviderByID(ctx context.Context, providerID uuid.UUID) (database.AIProvider, error) {
|
|
provider, err := p.db.GetAIProviderByID(ctx, providerID)
|
|
if err != nil {
|
|
return database.AIProvider{}, xerrors.Errorf("get AI provider: %w", err)
|
|
}
|
|
if !provider.Enabled {
|
|
return database.AIProvider{}, xerrors.Errorf("AI provider %s is disabled", provider.ID)
|
|
}
|
|
return provider, nil
|
|
}
|
|
|
|
func newLanguageModel(
|
|
providerHint string,
|
|
modelName string,
|
|
providerKeys chatprovider.ProviderAPIKeys,
|
|
userAgent string,
|
|
extraHeaders map[string]string,
|
|
httpClient *http.Client,
|
|
openAIConfig *codersdk.ChatModelOpenAIConfig,
|
|
) (chatprovider.Model, error) {
|
|
model, err := chatprovider.ModelFromConfig(
|
|
providerHint,
|
|
modelName,
|
|
providerKeys,
|
|
userAgent,
|
|
extraHeaders,
|
|
httpClient,
|
|
openAIConfig,
|
|
)
|
|
if err != nil {
|
|
return chatprovider.Model{}, err
|
|
}
|
|
if !model.Valid() {
|
|
provider, resolvedModel, resolveErr := chatprovider.ResolveModelWithProviderHint(modelName, providerHint)
|
|
if resolveErr != nil {
|
|
return chatprovider.Model{}, resolveErr
|
|
}
|
|
return chatprovider.Model{}, xerrors.Errorf(
|
|
"create model for %s/%s returned nil",
|
|
provider,
|
|
resolvedModel,
|
|
)
|
|
}
|
|
return model, nil
|
|
}
|