This commit is contained in:
2026-08-23 12:56:19 +02:00
parent 5340158228
commit ee1405cc1a
7 changed files with 260 additions and 29 deletions
+4 -4
View File
@@ -35,8 +35,8 @@ func TestResolveCompleterForRole_unsetFallsBackToEnv(t *testing.T) {
if !ok || oc == nil || !oc.Enabled() {
t.Fatalf("expected enabled OpenAIClient, got %T", c)
}
if label != ModeInternalLabel {
t.Fatalf("label=%q", label)
if label != ModeCustomLabel {
t.Fatalf("label=%q want %q (env bootstrap is custom)", label, ModeCustomLabel)
}
if byok {
t.Fatal("env fallback must not be BYOK")
@@ -97,8 +97,8 @@ func TestResolveCompleterForRole_platformProcessingRole(t *testing.T) {
if oc.APIKey != "sk-plat-processing" || oc.Model != "plat-model" {
t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model)
}
if label != ModeInternalLabel || byok {
t.Fatalf("label=%q byok=%v", label, byok)
if label != ModeCustomLabel || byok {
t.Fatalf("label=%q byok=%v want custom (loopback platform base)", label, byok)
}
}
+24 -6
View File
@@ -389,9 +389,14 @@ func (s *Service) TestPlatformRole(ctx context.Context, role string) (map[string
out["message"] = "AI role is not configured (or disabled) in admin platform settings"
return out, nil
}
if _, err := completer.Complete(ctx, "Reply with exactly: ok", "ping"); err != nil {
if err := probeCompleter(ctx, completer); err != nil {
out["status"] = "failed"
out["message"] = "connection failed — check provider, key, base URL, and model"
// Prefer classified probe detail (no secrets / model-id dumps) over a generic blur.
msg := processing.TruncateError(err)
if msg == "" || msg == "processing_failed" || msg == "provider error (details redacted)" {
msg = "connection failed — check provider, key, base URL, and model"
}
out["message"] = msg
return out, err
}
out["status"] = "ok"
@@ -399,6 +404,21 @@ func (s *Service) TestPlatformRole(ctx context.Context, role string) (map[string
return out, nil
}
// probeCompleter runs a minimal chat completion with GPT-5.6-safe options
// (max_completion_tokens + reasoning_effort=none, no custom temperature).
func probeCompleter(ctx context.Context, completer processing.Completer) error {
if completer == nil {
return ErrNotConfigured
}
opts := processing.ProbeCompleteOptions()
if co, ok := completer.(processing.CompleterWithOptions); ok {
_, err := co.CompleteWithOptions(ctx, "Reply with exactly: ok", "ping", opts)
return err
}
_, err := completer.Complete(ctx, "Reply with exactly: ok", "ping")
return err
}
// TestConnection sends a minimal chat completion and records last_test_*.
func (s *Service) TestConnection(ctx context.Context, companyID uuid.UUID) (map[string]any, error) {
ctx, cancel := context.WithTimeout(ctx, aiProbeTimeout)
@@ -422,12 +442,10 @@ func (s *Service) TestConnection(ctx context.Context, companyID uuid.UUID) (map[
WHERE company_id = $1`, companyID, status)
return map[string]any{"status": status, "message": message, "mode": resolved.ModeLabel}, ErrNotConfigured
}
_, err = resolved.Completer.Complete(ctx, "Reply with exactly: ok", "ping")
if err != nil {
if err := probeCompleter(ctx, resolved.Completer); err != nil {
status = "failed"
// TruncateError classifies transport/auth failures without leaking secrets.
message = processing.TruncateError(err)
if message == "" || message == "provider error (details redacted)" {
if message == "" || message == "processing_failed" || message == "provider error (details redacted)" {
message = "connection failed — check provider, key, base URL, and model"
}
_, _ = s.Pool.Exec(ctx, `