Skip to content

AI Classification

How it works

When a task is created, Doto fires an async classification job (non-blocking — the task is returned immediately):

  1. classifyTask(provider, { title, description }) is called
  2. Two requests run in parallel: classify() for tags, suggestPriority() for priority
  3. Results are written back to the task: tags and aiClassification

If the provider is null (not configured) or any request fails or times out (30s), the task is left with empty tags and aiClassification: null. Task creation never fails due to AI errors.

Configuration

Set these environment variables:

bash
# Explicit provider and model
AI_PROVIDER=openai
AI_MODEL=gpt-4o-mini        # optional, auto-selects if omitted
OPENAI_API_KEY=sk-...

# Or use Anthropic
AI_PROVIDER=anthropic       ANTHROPIC_API_KEY=sk-ant-...

# Optional — override API endpoints (e.g. a proxy or gateway)
OPENAI_BASE_URL=https://my-proxy.example.com/v1
ANTHROPIC_BASE_URL=https://my-proxy.example.com

If AI_PROVIDER is not set, the factory auto-detects based on which key is present (OpenAI → Anthropic). OPENAI_BASE_URL / ANTHROPIC_BASE_URL are optional; when unset, each provider's default endpoint is used.

The AI layer is powered by the Vercel AI SDK, using the first-party OpenAI and Anthropic providers.

The provider interface

typescript
// packages/server/src/ai/provider.ts
export interface AiProvider {
  classify(task: { title: string; description?: string }): Promise<ClassificationResult | null>;
  suggestPriority(task: { title: string; description?: string }): Promise<TaskPriority | null>;
}

Both methods must return null on any failure — never throw.

Adding a new provider

  1. Create packages/server/src/ai/providers/<name>.ts implementing AiProvider
  2. Handle the 30s timeout using AbortController
  3. Catch all errors and return null
  4. Register in src/ai/factory.ts:
typescript
if (provider === 'myprovider') {
  const apiKey = process.env.MYPROVIDER_API_KEY;
  if (!apiKey) return null;
  return new MyProvider(apiKey);
}

Stored data

aiClassification is a JSON field on the task:

json
{
  "tags": ["bug", "frontend", "auth"],
  "suggestedPriority": "P1",
  "reasoning": "Login issues are high priority user-facing bugs"
}

suggestedPriority is stored but never auto-applied to the task's priority field. The user decides whether to act on it.

Released under the MIT License.