Add common catalog query layer and enricher service

Introduce an API-style data layer for the global common tracker pattern
and common third party catalogs: typed filters, order fields, CursorKey,
cursor-paginated Load and CountAll, plus by-id enrichment re-queue and a
scoped reset/remap helper for a banner's tracker patterns. These reuse
the same page.Cursor/filter/order types the GraphQL API consumes, so a
future proboctl API can back them unchanged.

Extract the common-pattern enrichment logic out of the worker into a
CommonPatternEnricher service so it can run either from the background
queue or synchronously over a known set of ids; the worker becomes a
thin poller that delegates to it.

Extract the LLM client and tracker-agents config wiring into
pkg/agentsbuild so probod and other binaries build agents identically;
probod now delegates to it.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-03 13:37:36 +02:00
parent aebb2a1ed0
commit 662c0ae428
13 changed files with 1452 additions and 335 deletions

View File

@@ -15,20 +15,17 @@
package probod
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"go.gearno.de/kit/httpclient"
"go.gearno.de/kit/log"
"go.opentelemetry.io/otel/trace"
"go.probo.inc/probo/pkg/agentsbuild"
"go.probo.inc/probo/pkg/llm"
llmanthropic "go.probo.inc/probo/pkg/llm/anthropic"
llmopenai "go.probo.inc/probo/pkg/llm/openai"
)
// resolveAgentClient resolves the agent's effective config from defaults and
// builds an LLM client for it. The name parameter is used in the logger and
// in error messages.
// in error messages. It delegates to pkg/agentsbuild so probod and proboctl
// wire LLM clients identically.
func (impl *Implm) resolveAgentClient(
name string,
agent LLMAgentConfig,
@@ -36,61 +33,5 @@ func (impl *Implm) resolveAgentClient(
tp trace.TracerProvider,
r prometheus.Registerer,
) (LLMAgentConfig, *llm.Client, error) {
resolved := impl.cfg.Agents.ResolveAgent(agent)
providerCfg, ok := impl.cfg.Agents.Providers[resolved.Provider]
if !ok {
return LLMAgentConfig{}, nil, fmt.Errorf("unknown LLM provider %q for %s agent", resolved.Provider, name)
}
client, err := buildLLMClient(providerCfg, l.Named("llm."+name), tp, r)
if err != nil {
return LLMAgentConfig{}, nil, fmt.Errorf("cannot create %s LLM client: %w", name, err)
}
return resolved, client, nil
}
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(
httpclient.WithLogger(l),
httpclient.WithTracerProvider(tp),
httpclient.WithRegisterer(r),
)
switch providerType {
case "openai":
p := llmopenai.NewProvider(
cfg.APIKey,
llmopenai.WithHTTPClient(httpClient),
)
return llm.NewClient(
p,
"openai",
llm.WithLogger(l),
llm.WithTracerProvider(tp),
), nil
case "anthropic":
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 type: %q", providerType)
}
return agentsbuild.ResolveAgentClient(impl.cfg.Agents, name, agent, l, tp, r)
}