Add AI-powered evidence description generation

Introduce a background worker that automatically generates
compliance-focused descriptions for uploaded evidence files
using configurable LLM providers. Descriptions are surfaced
across all interfaces: GraphQL API, MCP API, CLI, and the
console UI.

Key changes:
- Multi-provider LLM config with per-agent settings (pointer
  types for Temperature/MaxTokens to preserve zero values)
- Evidence description worker with bounded concurrency
- EvidenceDescriptionStatus typed enum with PostgreSQL enum type
- New `prb evidence` CLI commands (list, view, delete)
- Evidence description displayed in console table and preview
- Migration only marks evidences without files as completed

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-26 14:30:04 +01:00
parent 8bbed534a9
commit 619ec7b882
24 changed files with 1390 additions and 125 deletions

View File

@@ -22,13 +22,14 @@ import (
"go.gearno.de/kit/log"
"go.opentelemetry.io/otel/trace"
"go.probo.inc/probo/pkg/llm"
llmanthropic "go.probo.inc/probo/pkg/llm/anthropic"
llmopenai "go.probo.inc/probo/pkg/llm/openai"
)
func buildLLMClient(cfg LLMConfig, l *log.Logger, tp trace.TracerProvider, r prometheus.Registerer) (*llm.Client, error) {
provider := cfg.Provider
if provider == "" {
provider = "openai"
func buildLLMClient(cfg LLMProviderConfig, l *log.Logger, tp trace.TracerProvider, r prometheus.Registerer) (*llm.Client, error) {
providerType := cfg.Type
if providerType == "" {
providerType = "openai"
}
httpClient := httpclient.DefaultPooledClient(
@@ -37,7 +38,7 @@ func buildLLMClient(cfg LLMConfig, l *log.Logger, tp trace.TracerProvider, r pro
httpclient.WithRegisterer(r),
)
switch provider {
switch providerType {
case "openai":
p := llmopenai.NewProvider(
cfg.APIKey,
@@ -50,10 +51,19 @@ func buildLLMClient(cfg LLMConfig, l *log.Logger, tp trace.TracerProvider, r pro
llm.WithTracerProvider(tp),
), nil
case "anthropic":
return nil, fmt.Errorf("anthropic provider not yet wired; add import and construct here")
p := llmanthropic.NewProvider(
cfg.APIKey,
llmanthropic.WithHTTPClient(httpClient),
)
return llm.NewClient(
p,
"anthropic",
llm.WithLogger(l),
llm.WithTracerProvider(tp),
), nil
case "bedrock":
return nil, fmt.Errorf("bedrock provider not yet wired; requires aws.Config")
default:
return nil, fmt.Errorf("unsupported LLM provider: %q", provider)
return nil, fmt.Errorf("unsupported LLM provider type: %q", providerType)
}
}