gadget/packages/ai
Rob Colbert 56f35a2fdf feat: Qdrant semantic search over chat history
Adds vector-based semantic search across all chat sessions using Qdrant.
When a ChatTurn finishes, its content is chunked, embedded, and upserted
to a Qdrant collection. A search API and UI components enable searching
at user, project, and session scope.

Phase 1 — Configuration & Dependencies
- Add port/apiKey to GadgetCodeConfig.qdrant type
- Uncomment and update qdrant section in YAML config example
- Add qdrant config passthrough in env.ts
- Add @qdrant/js-client-rest dependency

Phase 2 — AI Embedding API (@gadget/ai)
- Add IAiEmbeddingResponse interface and abstract embeddings() to AiApi
- Implement embeddings() in OllamaAiApi (client.embeddings)
- Implement embeddings() in OpenAiApi (client.embeddings.create)
- Export IAiEmbeddingResponse from package index

Phase 3 — Backend Vector Store Service
- Create VectorStoreService (ingestTurn, search, removeTurnPoints)
- Hook fire-and-forget ingest after turn.save() in drone-session
- Register VectorStoreService in service startup/shutdown

Phase 4 — Backend Search API
- Create POST /api/v1/search controller with userId enforcement
- Batch-hydrate results from MongoDB (user, project, session, turn)
- Register search route in v1 API router

Phase 5 — Frontend Search Components
- SearchInput: debounced input with lucide-react icons
- ChatSearchResults: modal with score badges, metadata, loading states
- DroneSelectionModal: drone picker for sessions without a drone
- Add searchApi and ISearchResult to API client
- Add search to Home (global), ProjectManager (project), ChatSessionView (session)
- Add id=turn-{turnId} to ChatTurn for scroll targeting
- Scroll-to-turn from search result selection and router state
- Show DroneSelectionModal when no drone available
- Add Select Drone button in ChatSessionView sidebar
2026-05-19 14:28:30 -04:00
..
src feat: Qdrant semantic search over chat history 2026-05-19 14:28:30 -04:00
types created by merging gadget-code and gadget-drone 2026-04-28 09:20:37 -04:00
.gitignore cleanup 2026-05-07 00:59:15 -04:00
package.json streaming response fixes (Ollama) 2026-05-08 02:02:17 -04:00
README.md more documentation and progress working towards a usable socket protocol 2026-04-29 15:23:03 -04:00
tsconfig.json created by merging gadget-code and gadget-drone 2026-04-28 09:20:37 -04:00
vitest.config.ts streaming response fixes (Ollama) 2026-05-08 02:02:17 -04:00

@gadget/ai

Gadget Code's AI API abstraction layer. Provides a single internal API contract for calling AI providers (Ollama, OpenAI) without consumer code knowing which provider is configured.

Principles

  1. One interface, all providers. Consumer code calls createAiApi() once and holds the resulting AiApi. It never checks provider.sdk again.
  2. All AI SDK knowledge is contained here. No consumer imports ollama or openai SDKs directly.
  3. Responses are normalized. All provider responses are translated to Gadget Code's internal interface types before returning.

Usage

import { createAiApi } from "@gadget/ai";

const provider = {
  _id: "local-ollama",
  name: "Local Ollama",
  sdk: "ollama",
  baseUrl: "http://localhost:11434",
  apiKey: "",
};

const modelConfig = {
  provider,
  modelId: "llama3.2",
  params: {
    reasoning: false,
    temperature: 0.8,
    topP: 0.9,
    topK: 40,
  },
};

const ai = createAiApi(provider, logger);

const result = await ai.generate(modelConfig, {
  prompt: "Explain what this code does",
  systemPrompt: "You are a code reviewer.",
});
console.log(result.response);
console.log(result.stats.duration.text); // formatted, e.g. "00:00:02"

API

Factory

createAiApi(provider, logger?) — Returns an AiApi instance for the given provider. logger is optional and defaults to a no-op logger. Pass your own logger to receive debug output.

AiApi

Abstract base class. Currently implemented:

  • OllamaAiApi — Ollama provider
  • OpenAiApi — OpenAI provider (stubbed)

ai.generate(model, options, streamCallback?)

Single-prompt generation. Returns IAiGenerateResponse.

ai.chat(model, options, streamCallback?)

Chat with conversation history. Pass options.context for multi-turn对话. Returns IAiChatResponse.

Interfaces

All interfaces are exported for use by consumers:

  • IAiProvider — AI provider configuration
  • IAiModelConfig — Model + runtime parameters
  • IAiGenerateOptions / IAiGenerateResponse
  • IAiChatOptions / IAiChatResponse — includes tool_calls for function-calling models
  • IAiInferenceStats — token counts and duration (both raw seconds number and formatted text string)
  • IAiLogger — injectable logger interface (debug, info, warn, error)

Providers

Ollama

Configured via IAiProvider with sdk: "ollama". Uses the ollama npm package. Handles streaming responses and normalizes Ollama-specific response fields (thinking tokens, token counts, duration).

OpenAI

Configured via IAiProvider with sdk: "openai". Stubbed — chat() and generate() throw "Not yet implemented". Implement by wiring the openai npm package following the same pattern as OllamaAiApi.

Duration Formatting

The library uses numeral to provide a consistent formatted duration string (stats.duration.text) in hh:mm:ss format. The raw nanosecond value is also returned in stats.duration.seconds for consumers that need the raw number.

Adding a New Provider

  1. Create packages/ai/src/<provider>.ts — extend AiApi, implement all abstract methods
  2. Update packages/ai/src/index.ts — add the new class to the createAiApi factory switch
  3. Update this README

No consumer code changes required.