diff --git a/adapters/models/openai_compatible_local.go b/adapters/models/openai_compatible_local.go index 36dcbcc..a07bf85 100644 --- a/adapters/models/openai_compatible_local.go +++ b/adapters/models/openai_compatible_local.go @@ -32,6 +32,8 @@ type OpenAICompatibleLocalAdapter struct { // qualification/evaluation in the capability registry. const compatibleDefaultContextBytes int64 = 32 << 10 +const compatibleAdvisoryMaxTokens = 32 + func NewOpenAICompatibleLocalAdapter(raw string, transport http.RoundTripper) (*OpenAICompatibleLocalAdapter, error) { return NewOpenAICompatibleLocalAdapterWithAPIKey(raw, "", transport) } @@ -157,7 +159,11 @@ func (a *OpenAICompatibleLocalAdapter) Advise(ctx context.Context, request cogni if err := ValidateAdvisoryRequest(request); err != nil || request.ProviderModelID == "" { return cognitive.AdvisoryResponse{}, ErrInvalidAdvisory } - payload := compatibleChatRequest{Model: string(request.ProviderModelID), Messages: []compatibleMessage{{Role: "system", Content: "Ты отвечаешь от имени Домового — локального помощника пользователя. Не называй себя именем модели, поставщиком или внутренним runtime. Если спрашивают имя, ответь: «Меня зовут Домовой». Понимай смысл сообщения и помогай довести цель до полезного результата. По умолчанию отвечай кратко, примерно одним-тремя предложениями; подробности давай только по просьбе. Отвечай только некатегоричным текстом совета; не утверждай, что внешнее действие уже выполнено."}, {Role: "user", Content: localAdvisoryMessage(request)}}, Temperature: 0, Stream: false, MaxTokens: request.MaxOutputBytes / 4, ChatTemplateKwargs: compatibleTemplateKwargs{EnableThinking: false}} + maxTokens := request.MaxOutputBytes / 4 + if maxTokens <= 0 || maxTokens > compatibleAdvisoryMaxTokens { + maxTokens = compatibleAdvisoryMaxTokens + } + payload := compatibleChatRequest{Model: string(request.ProviderModelID), Messages: []compatibleMessage{{Role: "system", Content: "Ты отвечаешь от имени Домового — локального помощника пользователя. Не называй себя именем модели, поставщиком или внутренним runtime. Если спрашивают имя, ответь: «Меня зовут Домовой». Понимай смысл сообщения и помогай довести цель до полезного результата. По умолчанию отвечай кратко, примерно одним-тремя предложениями; подробности давай только по просьбе. Отвечай только некатегоричным текстом совета; не утверждай, что внешнее действие уже выполнено."}, {Role: "user", Content: localAdvisoryMessage(request)}}, Temperature: 0, Stream: false, MaxTokens: maxTokens, ChatTemplateKwargs: compatibleTemplateKwargs{EnableThinking: false}} var result compatibleChatResponse status, err := a.requestJSON(ctx, http.MethodPost, "/chat/completions", payload, &result) if err != nil { diff --git a/adapters/models/openai_compatible_local_test.go b/adapters/models/openai_compatible_local_test.go index b90e6c0..71b282f 100644 --- a/adapters/models/openai_compatible_local_test.go +++ b/adapters/models/openai_compatible_local_test.go @@ -140,6 +140,7 @@ func TestOpenAICompatibleLocalAdapterDisablesTemplateThinkingForBoundedAdvisory( t.Fatal(err) } var payload struct { + MaxTokens int `json:"max_tokens"` ChatTemplateKwargs struct { EnableThinking bool `json:"enable_thinking"` } `json:"chat_template_kwargs"` @@ -147,6 +148,9 @@ func TestOpenAICompatibleLocalAdapterDisablesTemplateThinkingForBoundedAdvisory( if err := json.Unmarshal(transport.body, &payload); err != nil { t.Fatal(err) } + if payload.MaxTokens != compatibleAdvisoryMaxTokens { + t.Fatalf("max_tokens=%d, want bounded %d", payload.MaxTokens, compatibleAdvisoryMaxTokens) + } if payload.ChatTemplateKwargs.EnableThinking { t.Fatal("expected bounded advisory to disable template thinking") }