2026-08-09 22:47:43 +02:00
|
|
|
package aiprovider
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/platformsettings"
|
|
|
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/processing"
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type stubRoleSource struct {
|
|
|
|
|
ep RoleEndpoint
|
|
|
|
|
ok bool
|
|
|
|
|
err error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s stubRoleSource) LookupRole(_ context.Context, _ uuid.UUID, _ string) (RoleEndpoint, bool, error) {
|
|
|
|
|
return s.ep, s.ok, s.err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveCompleterForRole_unsetFallsBackToEnv(t *testing.T) {
|
|
|
|
|
svc := &Service{
|
|
|
|
|
Env: EnvConfig{
|
|
|
|
|
OpenAIAPIKey: "sk-env-fallback",
|
|
|
|
|
OpenAIBaseURL: "https://api.openai.com/v1",
|
|
|
|
|
OpenAIModel: "gpt-4o-mini",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
c, label, byok, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleProcessing)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
oc, ok := c.(*processing.OpenAIClient)
|
|
|
|
|
if !ok || oc == nil || !oc.Enabled() {
|
|
|
|
|
t.Fatalf("expected enabled OpenAIClient, got %T", c)
|
|
|
|
|
}
|
2026-08-23 12:56:19 +02:00
|
|
|
if label != ModeCustomLabel {
|
|
|
|
|
t.Fatalf("label=%q want %q (env bootstrap is custom)", label, ModeCustomLabel)
|
2026-08-09 22:47:43 +02:00
|
|
|
}
|
|
|
|
|
if byok {
|
|
|
|
|
t.Fatal("env fallback must not be BYOK")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveCompleterForRole_usesRoleBindingWhenSet(t *testing.T) {
|
|
|
|
|
svc := &Service{
|
|
|
|
|
Env: EnvConfig{
|
|
|
|
|
OpenAIAPIKey: "sk-should-not-win",
|
|
|
|
|
OpenAIModel: "env-model",
|
|
|
|
|
},
|
|
|
|
|
Roles: stubRoleSource{
|
|
|
|
|
ok: true,
|
|
|
|
|
ep: RoleEndpoint{
|
|
|
|
|
APIKey: "sk-role-processing",
|
|
|
|
|
BaseURL: "https://role.example/v1",
|
|
|
|
|
Model: "role-model",
|
|
|
|
|
UsingBYOK: false,
|
|
|
|
|
ModeLabel: ModeInternalLabel,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
c, label, byok, err := svc.ResolveCompleterForRole(context.Background(), uuid.New(), RoleProcessing)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
oc, ok := c.(*processing.OpenAIClient)
|
|
|
|
|
if !ok || oc == nil {
|
|
|
|
|
t.Fatalf("type=%T", c)
|
|
|
|
|
}
|
|
|
|
|
if oc.APIKey != "sk-role-processing" || oc.Model != "role-model" {
|
|
|
|
|
t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model)
|
|
|
|
|
}
|
|
|
|
|
if label != ModeInternalLabel || byok {
|
|
|
|
|
t.Fatalf("label=%q byok=%v", label, byok)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveCompleterForRole_platformProcessingRole(t *testing.T) {
|
|
|
|
|
plat := platformsettings.NewService(nil, platformsettings.EnvConfig{
|
|
|
|
|
OpenAIAPIKey: "sk-plat-processing",
|
|
|
|
|
OpenAIBaseURL: "http://127.0.0.1:8767/v1",
|
|
|
|
|
OpenAIModel: "plat-model",
|
|
|
|
|
})
|
|
|
|
|
svc := &Service{
|
|
|
|
|
Platform: plat,
|
|
|
|
|
Env: EnvConfig{OpenAIAPIKey: "sk-should-not-win", OpenAIModel: "env-model"},
|
|
|
|
|
}
|
|
|
|
|
c, label, byok, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleProcessing)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
oc, ok := c.(*processing.OpenAIClient)
|
|
|
|
|
if !ok || oc == nil {
|
|
|
|
|
t.Fatalf("type=%T", c)
|
|
|
|
|
}
|
|
|
|
|
if oc.APIKey != "sk-plat-processing" || oc.Model != "plat-model" {
|
|
|
|
|
t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model)
|
|
|
|
|
}
|
2026-08-23 12:56:19 +02:00
|
|
|
if label != ModeCustomLabel || byok {
|
|
|
|
|
t.Fatalf("label=%q byok=%v want custom (loopback platform base)", label, byok)
|
2026-08-09 22:47:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveCompleterForRole_vectorizationUnsetNoChatFallback(t *testing.T) {
|
|
|
|
|
svc := &Service{
|
|
|
|
|
Env: EnvConfig{OpenAIAPIKey: "sk-env", OpenAIModel: "m"},
|
|
|
|
|
}
|
|
|
|
|
c, _, _, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleVectorization)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if c != nil {
|
|
|
|
|
t.Fatal("vectorization must not fall back to chat completer")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveEmbedderForRole_usesPlatformVectorization(t *testing.T) {
|
|
|
|
|
plat := platformsettings.NewService(nil, platformsettings.EnvConfig{
|
|
|
|
|
OpenAIAPIKey: "sk-embed-env",
|
|
|
|
|
OpenAIBaseURL: "http://127.0.0.1:8767/v1",
|
|
|
|
|
OpenAIEmbeddingModel: "text-embedding-3-small",
|
|
|
|
|
})
|
|
|
|
|
svc := &Service{Platform: plat}
|
|
|
|
|
emb, err := svc.ResolveEmbedderForRole(context.Background(), uuid.Nil, RoleVectorization)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
oc, ok := emb.(*processing.OpenAIClient)
|
|
|
|
|
if !ok || oc == nil || !oc.Enabled() {
|
|
|
|
|
t.Fatalf("expected OpenAIClient embedder, got %T", emb)
|
|
|
|
|
}
|
|
|
|
|
if oc.APIKey != "sk-embed-env" || oc.Model != "text-embedding-3-small" {
|
|
|
|
|
t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveCompleterForRole_supportUnsetNoEnvFallback(t *testing.T) {
|
|
|
|
|
svc := &Service{
|
|
|
|
|
Env: EnvConfig{OpenAIAPIKey: "sk-env", OpenAIModel: "m"},
|
|
|
|
|
}
|
|
|
|
|
c, _, _, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleSupport)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if c != nil {
|
|
|
|
|
t.Fatal("unset support must not fall back to processing/env completer")
|
|
|
|
|
}
|
|
|
|
|
}
|