fixes
This commit is contained in:
@@ -2,12 +2,39 @@ package processing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestHeuristicCompleter_thinProductInventFromAttrs(t *testing.T) {
|
||||
t.Parallel()
|
||||
h := HeuristicCompleter{}
|
||||
system := `Retail product copywriter. Schema: {"name":"string","description":"string"}`
|
||||
user := "Category: Monitors\nName: UltraView 27\nDesc: UltraView 27\nAttrs: {\"brand\":\"Acme\",\"size\":\"27 inch\",\"panel\":\"IPS\"}"
|
||||
comp, err := h.Complete(context.Background(), system, user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var out map[string]string
|
||||
if err := json.Unmarshal([]byte(comp.Text), &out); err != nil {
|
||||
t.Fatalf("json: %v text=%q", err, comp.Text)
|
||||
}
|
||||
if out["name"] != "UltraView 27" {
|
||||
t.Fatalf("name=%q", out["name"])
|
||||
}
|
||||
desc := out["description"]
|
||||
if desc == "" || strings.EqualFold(desc, "UltraView 27") || desc == "Product description" {
|
||||
t.Fatalf("desc=%q want invented non-title copy", desc)
|
||||
}
|
||||
if !strings.Contains(strings.ToLower(desc), "acme") && !strings.Contains(strings.ToLower(desc), "ips") && !strings.Contains(strings.ToLower(desc), "27") {
|
||||
t.Fatalf("desc=%q want attrs-derived facts", desc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewOpenAIClient_capsRetries(t *testing.T) {
|
||||
c := NewOpenAIClient("k", "https://api.openai.com/v1", "m", 0, 99)
|
||||
if c.MaxRetries != maxOpenAIRetries {
|
||||
@@ -107,3 +134,95 @@ func TestNewOpenAIClient_blocksPrivateDialInProduction(t *testing.T) {
|
||||
t.Fatalf("expected host is not allowed, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChoiceMessageText_prefersContentThenReasoningJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := choiceMessageText(json.RawMessage(`"{\"name\":\"A\",\"description\":\"B\"}"`), "", ""); got == "" {
|
||||
t.Fatal("expected content string")
|
||||
}
|
||||
reasoning := `Thinking…\nDraft JSON:\n{"name":"NOSILEC W53070","description":"Stenski nosilec za TV."}\nVerify…`
|
||||
got := choiceMessageText(json.RawMessage(`""`), reasoning, "")
|
||||
if !strings.Contains(got, `"name"`) || !strings.Contains(got, "NOSILEC") {
|
||||
t.Fatalf("got=%q want JSON from reasoning_content", got)
|
||||
}
|
||||
if got := choiceMessageText(json.RawMessage(`null`), "no json here", ""); got != "" {
|
||||
t.Fatalf("expected empty without JSON, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenAIClient_doComplete_emptyContentWithReasoningJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"model": "code-fast",
|
||||
"choices": []map[string]any{{
|
||||
"finish_reason": "length",
|
||||
"message": map[string]any{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"reasoning_content": `steps… {"name":"TV Mount","description":"A wall mount for TVs."} more`,
|
||||
},
|
||||
}},
|
||||
"usage": map[string]int{"prompt_tokens": 10, "completion_tokens": 50, "total_tokens": 60},
|
||||
})
|
||||
}))
|
||||
defer srv.Close()
|
||||
c := NewOpenAIClient("test-key", srv.URL, "code-fast", 0, 1)
|
||||
c.HTTPClient = srv.Client()
|
||||
comp, err := c.CompleteWithOptions(context.Background(), "sys", "user", CompleteOptions{MaxTokens: 350, Temperature: 0.2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(comp.Text, "TV Mount") {
|
||||
t.Fatalf("text=%q", comp.Text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenAIClient_doComplete_emptyContentLengthRetriesWithBudget(t *testing.T) {
|
||||
t.Parallel()
|
||||
calls := 0
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls++
|
||||
var req map[string]any
|
||||
_ = json.NewDecoder(r.Body).Decode(&req)
|
||||
maxTok, _ := req["max_tokens"].(float64)
|
||||
if calls == 1 {
|
||||
if maxTok != 350 {
|
||||
t.Errorf("first max_tokens=%v want 350", maxTok)
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"model": "code-fast",
|
||||
"choices": []map[string]any{{
|
||||
"finish_reason": "length",
|
||||
"message": map[string]any{"role": "assistant", "content": "", "reasoning_content": "still thinking"},
|
||||
}},
|
||||
"usage": map[string]int{"prompt_tokens": 1, "completion_tokens": 350, "total_tokens": 351},
|
||||
})
|
||||
return
|
||||
}
|
||||
if maxTok != float64(maxTokensReasoningBudget) {
|
||||
t.Errorf("retry max_tokens=%v want %d", maxTok, maxTokensReasoningBudget)
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"model": "code-fast",
|
||||
"choices": []map[string]any{{
|
||||
"finish_reason": "stop",
|
||||
"message": map[string]any{"role": "assistant", "content": `{"name":"X","description":"Y product description text here."}`},
|
||||
}},
|
||||
"usage": map[string]int{"prompt_tokens": 1, "completion_tokens": 40, "total_tokens": 41},
|
||||
})
|
||||
}))
|
||||
defer srv.Close()
|
||||
c := NewOpenAIClient("test-key", srv.URL, "code-fast", 0, 2)
|
||||
c.HTTPClient = srv.Client()
|
||||
comp, err := c.CompleteWithOptions(context.Background(), "sys", "user", CompleteOptions{MaxTokens: 350, Temperature: 0.2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 2 {
|
||||
t.Fatalf("calls=%d want 2", calls)
|
||||
}
|
||||
if !strings.Contains(comp.Text, "\"name\"") {
|
||||
t.Fatalf("text=%q", comp.Text)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user