Skip to content

Commit 7b75267

Browse files
GottZclaude
andcommitted
fix: wire Dream embed protocol through scheduler → RunDreamCycle
Dream now uses EmbedWithProtocol instead of Embed, routing keyword embeddings through the configured protocol (ollama or openai). CTX_DREAM_EMBED_PROTOCOL env var controls the wire format. With openai protocol + llama-embed sidecar, Dream embeddings run on CPU via /v1/embeddings — verified in logs: llama-embed receives POST /v1/embeddings from ctx container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9fb7885 commit 7b75267

File tree

5 files changed

+33
-23
lines changed

5 files changed

+33
-23
lines changed

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ services:
109109
# volumes:
110110
# - /opt/models:/models:ro
111111
# command: >
112-
# --model /models/qwen3-embedding-8b.gguf
112+
# --model /models/Qwen3-Embedding-8B-Q4_K_M.gguf
113113
# --embedding
114114
# --pooling last
115115
# --port 8081

go/cmd/ctxd/config.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ type Config struct {
4848
DreamModel string
4949
DreamNumCtx int
5050
DreamThink string // "true", "false", or "" (fallback: ChatThink)
51-
DreamEmbedHost string // Separate embed host for Dream (empty = EmbedHost)
52-
DreamEmbedAPIKey string
53-
DreamEmbedModel string
54-
DreamEmbedNumCtx int
51+
DreamEmbedHost string // Separate embed host for Dream (empty = EmbedHost)
52+
DreamEmbedAPIKey string
53+
DreamEmbedProtocol string // "ollama" or "openai" (empty = EmbedProtocol)
54+
DreamEmbedModel string
55+
DreamEmbedNumCtx int
5556

5657
// Timezone for temporal resolution (e.g. "Europe/Berlin").
5758
// Defaults to UTC. Ensures "heute" resolves correctly for the user's timezone.
@@ -143,10 +144,11 @@ func LoadConfig() (Config, error) {
143144
DreamModel: getEnv("CTX_DREAM_MODEL", ""),
144145
DreamNumCtx: dreamNumCtx,
145146
DreamThink: getEnv("CTX_DREAM_THINK", ""),
146-
DreamEmbedHost: getEnv("CTX_DREAM_EMBED_HOST", ""),
147-
DreamEmbedAPIKey: getEnv("CTX_DREAM_EMBED_API_KEY", ""),
148-
DreamEmbedModel: getEnv("CTX_DREAM_EMBED_MODEL", ""),
149-
DreamEmbedNumCtx: getEnvIntSafe("CTX_DREAM_EMBED_NUM_CTX", 0),
147+
DreamEmbedHost: getEnv("CTX_DREAM_EMBED_HOST", ""),
148+
DreamEmbedAPIKey: getEnv("CTX_DREAM_EMBED_API_KEY", ""),
149+
DreamEmbedProtocol: getEnv("CTX_DREAM_EMBED_PROTOCOL", ""),
150+
DreamEmbedModel: getEnv("CTX_DREAM_EMBED_MODEL", ""),
151+
DreamEmbedNumCtx: getEnvIntSafe("CTX_DREAM_EMBED_NUM_CTX", 0),
150152

151153
Timezone: tz,
152154

go/cmd/ctxd/main.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,19 @@ func main() {
9494
EmbedHost: cfg.EmbedHost,
9595
EmbedAPIKey: cfg.EmbedAPIKey,
9696
EmbedModel: cfg.EmbedModel,
97-
EmbedNumCtx: cfg.EmbedNumCtx,
97+
EmbedNumCtx: cfg.EmbedNumCtx,
98+
EmbedProtocol: cfg.EmbedProtocol,
9899
DreamHost: cfg.DreamHost,
99100
DreamAPIKey: cfg.DreamAPIKey,
100101
ChatModel: cfg.ChatModel,
101102
DreamModel: cfg.DreamModel,
102103
DreamThink: dreamThink,
103104
DreamNumCtx: cfg.DreamNumCtx,
104-
DreamEmbedHost: cfg.DreamEmbedHost,
105-
DreamEmbedAPIKey: cfg.DreamEmbedAPIKey,
106-
DreamEmbedModel: cfg.DreamEmbedModel,
107-
DreamEmbedNumCtx: cfg.DreamEmbedNumCtx,
105+
DreamEmbedHost: cfg.DreamEmbedHost,
106+
DreamEmbedAPIKey: cfg.DreamEmbedAPIKey,
107+
DreamEmbedProtocol: cfg.DreamEmbedProtocol,
108+
DreamEmbedModel: cfg.DreamEmbedModel,
109+
DreamEmbedNumCtx: cfg.DreamEmbedNumCtx,
108110
}
109111
scheduler := events.NewScheduler(pool, schedulerConfig)
110112
go scheduler.Run(ctx)

go/internal/dream/dream.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const CycleTimeout = 180 * time.Second
5050

5151
// RunDreamCycle executes one dream cycle: pick → keywords → search → evaluate → link.
5252
// Returns the number of links created, or 0 if no block was available.
53-
func RunDreamCycle(ctx context.Context, pool *pgxpool.Pool, embedHost, embedAPIKey, embedModel string, embedNumCtx int, chatHost, chatAPIKey, chatModel string, think *bool, opts llm.Options, readScopes []string) (int, error) {
53+
func RunDreamCycle(ctx context.Context, pool *pgxpool.Pool, embedHost, embedAPIKey, embedProtocol, embedModel string, embedNumCtx int, chatHost, chatAPIKey, chatModel string, think *bool, opts llm.Options, readScopes []string) (int, error) {
5454
ctx, cancel := context.WithTimeout(ctx, CycleTimeout)
5555
defer cancel()
5656
// Step 1: Pick a block.
@@ -87,7 +87,7 @@ func RunDreamCycle(ctx context.Context, pool *pgxpool.Pool, embedHost, embedAPIK
8787
)
8888

8989
// Step 3: Search per keyword via RRF.
90-
candidates, err := searchByKeywords(ctx, pool, embedHost, embedAPIKey, embedModel, embedNumCtx, keywords, readScopes, block.ID, block.Scope)
90+
candidates, err := searchByKeywords(ctx, pool, embedHost, embedAPIKey, embedProtocol, embedModel, embedNumCtx, keywords, readScopes, block.ID, block.Scope)
9191
if err != nil {
9292
slog.Warn("dream: keyword search failed", "block_id", block.ID, "error", err)
9393
_ = SetDreamCooldown(ctx, pool, block.ID, CooldownInertDays)
@@ -221,15 +221,15 @@ func SetDreamCooldown(ctx context.Context, pool *pgxpool.Pool, blockID string, d
221221

222222
// searchByKeywords runs one RRF search per keyword, deduplicates results,
223223
// and returns candidate blocks (excluding the source block and cross-scope blocks).
224-
func searchByKeywords(ctx context.Context, pool *pgxpool.Pool, embedHost, embedAPIKey, embedModel string, embedNumCtx int, keywords []string, scopes []string, sourceID, sourceScope string) ([]BlockInfo, error) {
224+
func searchByKeywords(ctx context.Context, pool *pgxpool.Pool, embedHost, embedAPIKey, embedProtocol, embedModel string, embedNumCtx int, keywords []string, scopes []string, sourceID, sourceScope string) ([]BlockInfo, error) {
225225
seen := make(map[string]bool)
226226
seen[sourceID] = true // Exclude source block.
227227
var candidates []BlockInfo
228228
embedFailures := 0
229229

230230
for _, kw := range keywords {
231231
// Embed the keyword for semantic search.
232-
kwEmbedding, err := embed.Embed(ctx, embedHost, embedAPIKey, embedModel, kw, embed.PrefixQuery, embedNumCtx)
232+
kwEmbedding, err := embed.EmbedWithProtocol(ctx, embedProtocol, embedHost, embedAPIKey, embedModel, kw, embed.PrefixQuery, embedNumCtx)
233233
if err != nil {
234234
embedFailures++
235235
slog.Debug("dream: embed keyword failed", "keyword", kw, "error", err)

go/internal/events/scheduler.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ type Config struct {
4646
EmbedAPIKey string // Embedding provider API key (query path)
4747
EmbedModel string // Embedding model name (query path)
4848
EmbedNumCtx int // Embedding num_ctx (query path)
49-
DreamEmbedHost string // Dream embedding host (empty = EmbedHost)
50-
DreamEmbedAPIKey string // Dream embedding API key (empty = EmbedAPIKey)
51-
DreamEmbedModel string // Dream embedding model (empty = EmbedModel)
52-
DreamEmbedNumCtx int // Dream embedding num_ctx (0 = EmbedNumCtx)
49+
DreamEmbedHost string // Dream embedding host (empty = EmbedHost)
50+
DreamEmbedAPIKey string // Dream embedding API key (empty = EmbedAPIKey)
51+
DreamEmbedProtocol string // Dream embedding protocol (empty = EmbedProtocol)
52+
DreamEmbedModel string // Dream embedding model (empty = EmbedModel)
53+
DreamEmbedNumCtx int // Dream embedding num_ctx (0 = EmbedNumCtx)
54+
EmbedProtocol string // Query-path embed protocol ("ollama" or "openai")
5355
DreamHost string // Dream LLM provider host
5456
DreamAPIKey string // Dream LLM provider API key (empty = no auth)
5557
ChatModel string // Chat model name (fallback for dream)
@@ -286,6 +288,10 @@ func (s *Scheduler) runDreamCycle(dreamModel string, dreamOpts llm.Options) (int
286288
if embedAPIKey == "" {
287289
embedAPIKey = s.config.EmbedAPIKey
288290
}
291+
embedProtocol := s.config.DreamEmbedProtocol
292+
if embedProtocol == "" {
293+
embedProtocol = s.config.EmbedProtocol
294+
}
289295
embedModel := s.config.DreamEmbedModel
290296
if embedModel == "" {
291297
embedModel = s.config.EmbedModel
@@ -297,7 +303,7 @@ func (s *Scheduler) runDreamCycle(dreamModel string, dreamOpts llm.Options) (int
297303

298304
return dream.RunDreamCycle(
299305
dreamCtx, s.pool,
300-
embedHost, embedAPIKey, embedModel, embedNumCtx,
306+
embedHost, embedAPIKey, embedProtocol, embedModel, embedNumCtx,
301307
s.config.DreamHost, s.config.DreamAPIKey, dreamModel,
302308
s.config.DreamThink, dreamOpts,
303309
s.config.ReadScopes,

0 commit comments

Comments
 (0)