package evolve import ( "context" "testing" "time" "git.flytoex.net/yuanwei/flyto-agent/pkg/flyto" ) // TestFlytoLLMClient_NonZeroTemperature_TranslatesToPointer 锁 adapter 翻译: // 非零 LLMCallOpts.Temperature 进 flyto.Request.Temperature 为 *float64, // 值等于 opts.Temperature. // // Locks the adapter translation: a non-zero LLMCallOpts.Temperature ends up // as a non-nil flyto.Request.Temperature whose deref equals opts.Temperature. func TestFlytoLLMClient_NonZeroTemperature_TranslatesToPointer(t *testing.T) { p := &fakeProvider{events: []flyto.Event{&flyto.TextEvent{Text: "ok"}}} c := newTestClient(t, p) if _, err := c.Complete(context.Background(), "p", LLMCallOpts{Temperature: 0.7}); err != nil { t.Fatalf("Complete: %v", err) } if p.lastReq.Temperature == nil { t.Fatal("flyto.Request.Temperature = nil, want non-nil for opts.Temperature=0.7") } if *p.lastReq.Temperature != 0.7 { t.Errorf("*flyto.Request.Temperature = %v, want 0.7", *p.lastReq.Temperature) } } // TestFlytoLLMClient_NonZeroTopP_TranslatesToPointer 锁 TopP 翻译同形. // // Locks the symmetric TopP translation. func TestFlytoLLMClient_NonZeroTopP_TranslatesToPointer(t *testing.T) { p := &fakeProvider{events: []flyto.Event{&flyto.TextEvent{Text: "ok"}}} c := newTestClient(t, p) if _, err := c.Complete(context.Background(), "p", LLMCallOpts{TopP: 0.85}); err != nil { t.Fatalf("Complete: %v", err) } if p.lastReq.TopP == nil { t.Fatal("flyto.Request.TopP = nil, want non-nil for opts.TopP=0.85") } if *p.lastReq.TopP != 0.85 { t.Errorf("*flyto.Request.TopP = %v, want 0.85", *p.lastReq.TopP) } } // TestFlytoLLMClient_ZeroTemperature_LeavesRequestNil 锁 zero-as-unset 语义: // LLMCallOpts.Temperature == 0 → flyto.Request.Temperature 保持 nil 让上游 // provider 用自己默认 (与 LLMCallOpts godoc 的 zero-value 约定一致). // // Locks the zero-as-unset semantic: opts.Temperature = 0 leaves // flyto.Request.Temperature nil so the upstream provider applies its own // default. Aligns with the LLMCallOpts godoc zero-value convention. func TestFlytoLLMClient_ZeroTemperature_LeavesRequestNil(t *testing.T) { p := &fakeProvider{events: []flyto.Event{&flyto.TextEvent{Text: "ok"}}} c := newTestClient(t, p) if _, err := c.Complete(context.Background(), "p", LLMCallOpts{}); err != nil { t.Fatalf("Complete: %v", err) } if p.lastReq.Temperature != nil { t.Errorf("flyto.Request.Temperature = %v, want nil (zero opts → unset)", *p.lastReq.Temperature) } if p.lastReq.TopP != nil { t.Errorf("flyto.Request.TopP = %v, want nil (zero opts → unset)", *p.lastReq.TopP) } } // TestFlytoLLMClient_BothSamplingKnobs_Independent 锁 Temperature 与 TopP // 翻译相互独立: 只设 Temperature 不影响 TopP nil 状态, 反之亦然. // // Locks the independence: setting only Temperature must not pollute TopP // (and vice versa), or callers cannot reason about each knob in isolation. func TestFlytoLLMClient_BothSamplingKnobs_Independent(t *testing.T) { p := &fakeProvider{events: []flyto.Event{&flyto.TextEvent{Text: "ok"}}} c := newTestClient(t, p) if _, err := c.Complete(context.Background(), "p", LLMCallOpts{Temperature: 0.5}); err != nil { t.Fatalf("Complete: %v", err) } if p.lastReq.Temperature == nil || *p.lastReq.Temperature != 0.5 { t.Errorf("Temperature: got %v, want 0.5", p.lastReq.Temperature) } if p.lastReq.TopP != nil { t.Errorf("TopP polluted by Temperature setting: got %v, want nil", *p.lastReq.TopP) } } // TestWithTopP_WritesGenConfig 锁 GenOpt 写入: WithTopP(p) 调用后 // genConfig.TopP == p (mirrors WithTemperature 同 case 防漏写). // // Locks GenOpt write: WithTopP(p) sets genConfig.TopP = p (mirrors the // existing WithTemperature test pattern to guard against silent omissions). func TestWithTopP_WritesGenConfig(t *testing.T) { cfg := genConfig{} WithTopP(0.92)(&cfg) if cfg.TopP != 0.92 { t.Errorf("genConfig.TopP = %v, want 0.92", cfg.TopP) } } // TestBuildMeta_RecordsTopP 锁 buildMeta 把 cfg.TopP 写入 meta["top_p"]. // ParameterEvolver 与下游 evaluator 依赖完整 sampling 配置追溯候选. // // Locks buildMeta recording cfg.TopP under meta["top_p"]. ParameterEvolver // and downstream evaluators depend on the full sampling config to trace // any candidate. func TestBuildMeta_RecordsTopP(t *testing.T) { cfg := genConfig{Temperature: 0.7, TopP: 0.95} meta := buildMeta(nil, cfg, "test-model", time.Now().UTC()) v, ok := meta["top_p"] if !ok { t.Fatal("meta missing top_p key") } if vf, ok := v.(float64); !ok || vf != 0.95 { t.Errorf("meta[top_p] = %v (%T), want 0.95 (float64)", v, v) } // temperature 不能被 top_p 引入污染 -- 旧 case 也应仍工作. if vt := meta["temperature"]; vt != 0.7 { t.Errorf("meta[temperature] = %v, want 0.7", vt) } }