refactor(ai): Implement unified AI service layer and fix subtask update
- Unified Service: Introduced 'scripts/modules/ai-services-unified.js' to centralize AI interactions using provider modules ('src/ai-providers/') and the Vercel AI SDK.
- Provider Modules: Implemented 'anthropic.js' and 'perplexity.js' wrappers for Vercel SDK.
- 'updateSubtaskById' Fix: Refactored the AI call within 'updateSubtaskById' to use 'generateTextService' from the unified layer, resolving runtime errors related to parameter passing and streaming. This serves as the pattern for refactoring other AI calls in 'scripts/modules/task-manager/'.
- Task Status: Marked Subtask 61.19 as 'done'.
- Rules: Added new 'ai-services.mdc' rule.
This centralizes AI logic, replacing previous direct SDK calls and custom implementations. API keys are resolved via 'resolveEnvVariable' within the service layer. The refactoring of 'updateSubtaskById' establishes the standard approach for migrating other AI-dependent functions in the task manager module to use the unified service.
Relates to Task 61.
This commit is contained in:
118
.cursor/rules/ai_services.mdc
Normal file
118
.cursor/rules/ai_services.mdc
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
description: Guidelines for interacting with the unified AI service layer.
|
||||
globs: scripts/modules/ai-services-unified.js, scripts/modules/task-manager/*.js, scripts/modules/commands.js
|
||||
---
|
||||
|
||||
# AI Services Layer Guidelines
|
||||
|
||||
This document outlines the architecture and usage patterns for interacting with Large Language Models (LLMs) via the Task Master's unified AI service layer. The goal is to centralize configuration, provider selection, API key management, fallback logic, and error handling.
|
||||
|
||||
**Core Components:**
|
||||
|
||||
* **Configuration (`.taskmasterconfig` & [`config-manager.js`](mdc:scripts/modules/config-manager.js)):**
|
||||
* Defines the AI provider and model ID for different roles (`main`, `research`, `fallback`).
|
||||
* Stores parameters like `maxTokens` and `temperature` per role.
|
||||
* Managed via `task-master models --setup`.
|
||||
* [`config-manager.js`](mdc:scripts/modules/config-manager.js) provides getters (e.g., `getMainProvider()`, `getMainModelId()`, `getParametersForRole()`) to access these settings.
|
||||
* API keys are **NOT** stored here; they are resolved via `resolveEnvVariable` from `.env` or MCP session env. See [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc).
|
||||
* Relies on `data/supported-models.json` for model validation and metadata.
|
||||
|
||||
* **Unified Service (`ai-services-unified.js`):**
|
||||
* Exports primary interaction functions: `generateTextService`, `streamTextService`, `generateObjectService`.
|
||||
* Contains the core `_unifiedServiceRunner` logic.
|
||||
* Uses `config-manager.js` getters to determine the provider/model based on the requested `role`.
|
||||
* Implements the fallback sequence (main -> fallback -> research or variations).
|
||||
* Constructs the `messages` array (`[{ role: 'system', ... }, { role: 'user', ... }]`) required by the Vercel AI SDK.
|
||||
* Calls internal retry logic (`_attemptProviderCallWithRetries`).
|
||||
* Resolves API keys via `_resolveApiKey`.
|
||||
* Maps requests to the correct provider implementation via `PROVIDER_FUNCTIONS`.
|
||||
|
||||
* **Provider Implementations (`src/ai-providers/*.js`):**
|
||||
* Contain provider-specific code (e.g., `src/ai-providers/anthropic.js`).
|
||||
* Import Vercel AI SDK provider adapters (`@ai-sdk/anthropic`, `@ai-sdk/perplexity`, etc.).
|
||||
* Wrap core Vercel AI SDK functions (`generateText`, `streamText`, `generateObject`).
|
||||
* Accept standard parameters (`apiKey`, `modelId`, `messages`, `maxTokens`, etc.).
|
||||
* Return results in the format expected by `_unifiedServiceRunner`.
|
||||
|
||||
**Usage Pattern (from Core Logic like `task-manager`):**
|
||||
|
||||
1. **Choose Service:** Decide whether you need a full text response (`generateTextService`) or a stream (`streamTextService`).
|
||||
* ✅ **DO**: **Prefer `generateTextService`** for interactions that send large context payloads (e.g., stringified JSON) and **do not** require incremental display in the UI. This is currently more reliable, especially if Anthropic is the configured provider.
|
||||
* ⚠️ **CAUTION**: `streamTextService` may be unreliable with the Vercel SDK's Anthropic adapter when sending large user messages. Use with caution or stick to `generateTextService` for such cases until SDK improvements are confirmed.
|
||||
|
||||
2. **Import Service:** Import the chosen service function from `../ai-services-unified.js`.
|
||||
```javascript
|
||||
// Preferred for updateSubtaskById, parsePRD, etc.
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
|
||||
// Use only if incremental display is implemented AND provider streaming is reliable
|
||||
// import { streamTextService } from '../ai-services-unified.js';
|
||||
```
|
||||
|
||||
3. **Prepare Parameters:** Construct the parameters object.
|
||||
* `role`: `'main'`, `'research'`, or `'fallback'`. Determines the initial provider/model attempt.
|
||||
* `session`: Pass the MCP `session` object if available (for API key resolution), otherwise `null` or omit.
|
||||
* `systemPrompt`: Your system instruction string.
|
||||
* `prompt`: The user message string (can be long, include stringified data, etc.).
|
||||
* (For `generateObjectService`): `schema`, `objectName`.
|
||||
|
||||
4. **Call Service:** Use `await` to call the service function.
|
||||
```javascript
|
||||
// Example using generateTextService
|
||||
try {
|
||||
const resultText = await generateTextService({
|
||||
role: 'main', // Or 'research'/'fallback'
|
||||
session: session, // Or null
|
||||
systemPrompt: "You are...",
|
||||
prompt: userMessageContent // Can include stringified JSON etc.
|
||||
});
|
||||
additionalInformation = resultText.trim();
|
||||
// ... process resultText ...
|
||||
} catch (error) {
|
||||
// Handle errors thrown if all providers/retries fail
|
||||
report(`AI service call failed: ${error.message}`, 'error');
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Example using streamTextService (Use with caution for Anthropic/large payloads)
|
||||
try {
|
||||
const streamResult = await streamTextService({
|
||||
role: 'main',
|
||||
session: session,
|
||||
systemPrompt: "You are...",
|
||||
prompt: userMessageContent
|
||||
});
|
||||
|
||||
// Check if a stream was actually returned (might be null if overridden)
|
||||
if (streamResult.textStream) {
|
||||
for await (const chunk of streamResult.textStream) {
|
||||
additionalInformation += chunk;
|
||||
}
|
||||
additionalInformation = additionalInformation.trim();
|
||||
} else if (streamResult.text) {
|
||||
// Handle case where generateText was used internally (Anthropic override)
|
||||
// NOTE: This override logic is currently REMOVED as we prefer generateTextService directly
|
||||
additionalInformation = streamResult.text.trim();
|
||||
} else {
|
||||
additionalInformation = ''; // Should not happen
|
||||
}
|
||||
// ... process additionalInformation ...
|
||||
} catch (error) {
|
||||
report(`AI service call failed: ${error.message}`, 'error');
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
5. **Handle Results/Errors:** Process the returned text/stream/object or handle errors thrown by the service layer.
|
||||
|
||||
**Key Implementation Rules & Gotchas:**
|
||||
|
||||
* ✅ **DO**: Centralize all AI calls through `generateTextService` / `streamTextService`.
|
||||
* ✅ **DO**: Ensure `.taskmasterconfig` has valid provider names, model IDs, and parameters (`maxTokens` appropriate for the model).
|
||||
* ✅ **DO**: Ensure API keys are correctly configured in `.env` / `.cursor/mcp.json`.
|
||||
* ✅ **DO**: Pass the `session` object to the service call if available (for MCP calls).
|
||||
* ❌ **DON'T**: Call Vercel AI SDK functions (`streamText`, `generateText`) directly from `task-manager` or commands.
|
||||
* ❌ **DON'T**: Implement fallback or retry logic outside `ai-services-unified.js`.
|
||||
* ❌ **DON'T**: Handle API key resolution outside the service layer.
|
||||
* ⚠️ **Streaming Caution**: Be aware of potential reliability issues using `streamTextService` with Anthropic/large payloads via the SDK. Prefer `generateTextService` for these cases until proven otherwise.
|
||||
* ⚠️ **Debugging Imports**: If you get `"X is not defined"` errors related to service functions, check for internal errors within `ai-services-unified.js` (like incorrect import paths or syntax errors).
|
||||
@@ -71,18 +71,40 @@ alwaysApply: false
|
||||
- `getStatusWithColor(status)`: Returns status string with color formatting.
|
||||
- `formatDependenciesWithStatus(dependencies, allTasks, inTable)`: Formats dependency list with status indicators.
|
||||
|
||||
- **[`ai-services.js`](mdc:scripts/modules/ai-services.js) (Conceptual): AI Integration**
|
||||
- **Purpose**: Abstracts interactions with AI models (like Anthropic Claude and Perplexity AI) for various features. *Note: This module might be implicitly implemented within `task-manager.js` and `utils.js` or could be explicitly created for better organization as the project evolves.*
|
||||
- **Responsibilities**:
|
||||
- Handles API calls to AI services.
|
||||
- Manages prompts and parameters for AI requests.
|
||||
- Parses AI responses and extracts relevant information.
|
||||
- Implements logic for task complexity analysis, task expansion, and PRD parsing using AI.
|
||||
- **Potential Functions**:
|
||||
- `getAIResponse(prompt, model, maxTokens, temperature)`: Generic function to interact with AI model.
|
||||
- `analyzeTaskComplexityWithAI(taskDescription)`: Sends task description to AI for complexity analysis.
|
||||
- `expandTaskWithAI(taskDescription, numSubtasks, researchContext)`: Generates subtasks using AI.
|
||||
- `parsePRDWithAI(prdContent)`: Extracts tasks from PRD content using AI.
|
||||
- **[`ai-services-unified.js`](mdc:scripts/modules/ai-services-unified.js): Unified AI Service Layer**
|
||||
- **Purpose**: Provides a centralized interface for interacting with various Large Language Models (LLMs) using the Vercel AI SDK.
|
||||
- **Responsibilities** (See also: [`ai_services.mdc`](mdc:.cursor/rules/ai_services.mdc)):
|
||||
- Exports primary functions (`generateTextService`, `streamTextService`, `generateObjectService`) for core modules to use.
|
||||
- Implements provider selection logic based on configuration roles (`main`, `research`, `fallback`) retrieved from [`config-manager.js`](mdc:scripts/modules/config-manager.js).
|
||||
- Manages API key resolution (via [`utils.js`](mdc:scripts/modules/utils.js)) from environment or MCP session.
|
||||
- Handles fallback sequences between configured providers.
|
||||
- Implements retry logic for specific API errors.
|
||||
- Constructs the `messages` array format required by the Vercel AI SDK.
|
||||
- Delegates actual API calls to provider-specific implementation modules.
|
||||
- **Key Components**:
|
||||
- `_unifiedServiceRunner`: Core logic for provider selection, fallback, and retries.
|
||||
- `PROVIDER_FUNCTIONS`: Map linking provider names to their implementation functions.
|
||||
- `generateTextService`, `streamTextService`, `generateObjectService`: Exported functions.
|
||||
|
||||
- **[`src/ai-providers/*.js`](mdc:src/ai-providers/): Provider-Specific Implementations**
|
||||
- **Purpose**: Contains the wrapper code for interacting with specific LLM providers via the Vercel AI SDK.
|
||||
- **Responsibilities** (See also: [`ai_services.mdc`](mdc:.cursor/rules/ai_services.mdc)):
|
||||
- Imports Vercel AI SDK provider adapters (e.g., `@ai-sdk/anthropic`).
|
||||
- Implements standardized functions (e.g., `generateAnthropicText`, `streamAnthropicText`) that wrap the core Vercel AI SDK functions (`generateText`, `streamText`).
|
||||
- Accepts standardized parameters (`apiKey`, `modelId`, `messages`, etc.) from `ai-services-unified.js`.
|
||||
- Returns results in the format expected by `ai-services-unified.js`.
|
||||
|
||||
- **[`config-manager.js`](mdc:scripts/modules/config-manager.js): Configuration Management**
|
||||
- **Purpose**: Manages loading, validation, and access to configuration settings, primarily from `.taskmasterconfig`.
|
||||
- **Responsibilities** (See also: [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc)):
|
||||
- Reads and parses the `.taskmasterconfig` file.
|
||||
- Merges file configuration with default values.
|
||||
- Provides getters for accessing specific configuration values (e.g., `getMainProvider()`, `getMainModelId()`, `getParametersForRole()`, `getLogLevel()`).
|
||||
- **Note**: Does *not* handle API key storage (keys are in `.env` or MCP `session.env`).
|
||||
- **Key Components**:
|
||||
- `getConfig()`: Loads and returns the merged configuration object.
|
||||
- Role-specific getters (e.g., `getMainProvider`, `getMainModelId`, `getMainMaxTokens`).
|
||||
- Global setting getters (e.g., `getLogLevel`, `getDebugFlag`).
|
||||
|
||||
- **[`utils.js`](mdc:scripts/modules/utils.js): Core Utility Functions**
|
||||
- **Purpose**: Provides low-level, reusable utility functions used across the **CLI application**. **Note:** Configuration management is now handled by [`config-manager.js`](mdc:scripts/modules/config-manager.js).
|
||||
@@ -153,10 +175,12 @@ alwaysApply: false
|
||||
|
||||
- **Commands Initiate Actions**: User commands entered via the CLI (parsed by `commander` based on definitions in [`commands.js`](mdc:scripts/modules/commands.js)) are the entry points for most operations.
|
||||
- **Command Handlers Delegate to Core Logic**: Action handlers within [`commands.js`](mdc:scripts/modules/commands.js) call functions in core modules like [`task-manager.js`](mdc:scripts/modules/task-manager.js), [`dependency-manager.js`](mdc:scripts/modules/dependency-manager.js), and [`init.js`](mdc:scripts/init.js) (for the `init` command) to perform the actual work.
|
||||
- **UI for Presentation**: [`ui.js`](mdc:scripts/modules/ui.js) is used by command handlers and task/dependency managers to display information to the user. UI functions primarily consume data and format it for output, without modifying core application state.
|
||||
- **Utilities for Common Tasks**: [`utils.js`](mdc:scripts/modules/utils.js) provides helper functions used by all other modules for configuration, logging, file operations, and common data manipulations.
|
||||
- **AI Services Integration**: AI functionalities (complexity analysis, task expansion, PRD parsing) are invoked from [`task-manager.js`](mdc:scripts/modules/task-manager.js) and potentially [`commands.js`](mdc:scripts/modules/commands.js), likely using functions that would reside in a dedicated `ai-services.js` module or be integrated within `utils.js` or `task-manager.js`.
|
||||
- **MCP Server Interaction**: External tools interact with the `mcp-server`. MCP Tool `execute` methods use `getProjectRootFromSession` to find the project root, then call direct function wrappers (in `mcp-server/src/core/direct-functions/`) passing the root in `args`. These wrappers handle path finding for `tasks.json` (using `path-utils.js`), validation, caching, call the core logic from `scripts/modules/` (passing logging context via the standard wrapper pattern detailed in mcp.mdc), and return a standardized result. The final MCP response is formatted by `mcp-server/src/tools/utils.js`. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details.
|
||||
- **Core Logic Calls AI Service Layer**: Core modules requiring AI functionality (like [`task-manager.js`](mdc:scripts/modules/task-manager.js)) **import and call functions from the unified AI service layer (`ai-services-unified.js`)**, such as `generateTextService`.
|
||||
- **AI Service Layer Orchestrates**: [`ai-services-unified.js`](mdc:scripts/modules/ai-services-unified.js) uses [`config-manager.js`](mdc:scripts/modules/config-manager.js) to get settings, selects the appropriate provider function from [`src/ai-providers/*.js`](mdc:src/ai-providers/), resolves API keys (using `resolveEnvVariable` from [`utils.js`](mdc:scripts/modules/utils.js)), and handles fallbacks/retries.
|
||||
- **Provider Implementation Executes**: The selected function in [`src/ai-providers/*.js`](mdc:src/ai-providers/) interacts with the Vercel AI SDK core functions (`generateText`, `streamText`) using the Vercel provider adapters.
|
||||
- **UI for Presentation**: [`ui.js`](mdc:scripts/modules/ui.js) is used by command handlers and core modules to display information to the user. UI functions primarily consume data and format it for output.
|
||||
- **Utilities for Common Tasks**: [`utils.js`](mdc:scripts/modules/utils.js) provides helper functions (logging, file I/O, string manipulation, API key resolution) used by various modules.
|
||||
- **MCP Server Interaction**: External tools interact with the `mcp-server`. MCP Tool `execute` methods call direct function wrappers (`*Direct` functions) which then call the core logic from `scripts/modules/`. If AI is needed, the core logic calls the unified AI service layer as described above. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details.
|
||||
|
||||
## Silent Mode Implementation Pattern in MCP Direct Functions
|
||||
|
||||
@@ -349,6 +373,11 @@ The `initialize_project` command provides a way to set up a new Task Master proj
|
||||
- **CLI Command**: `task-master init`
|
||||
- **MCP Tool**: `initialize_project`
|
||||
- **Functionality**:
|
||||
- Creates necessary directories and files for a new project
|
||||
- Sets up `tasks.json` and initial task files
|
||||
- Configures project metadata (name, description, version)
|
||||
- Handles shell alias creation if requested
|
||||
- Works in both interactive and non-interactive modes
|
||||
- Creates necessary directories and files for a new project
|
||||
- Sets up `tasks.json` and initial task files
|
||||
- Configures project metadata (name, description, version)
|
||||
|
||||
@@ -25,11 +25,17 @@ alwaysApply: false
|
||||
The standard pattern for adding a feature follows this workflow:
|
||||
|
||||
1. **Core Logic**: Implement the business logic in the appropriate module (e.g., [`task-manager.js`](mdc:scripts/modules/task-manager.js)).
|
||||
2. **UI Components**: Add any display functions to [`ui.js`](mdc:scripts/modules/ui.js) following [`ui.mdc`](mdc:.cursor/rules/ui.mdc).
|
||||
3. **Command Integration**: Add the CLI command to [`commands.js`](mdc:scripts/modules/commands.js) following [`commands.mdc`](mdc:.cursor/rules/commands.mdc).
|
||||
4. **Testing**: Write tests for all components of the feature (following [`tests.mdc`](mdc:.cursor/rules/tests.mdc))
|
||||
5. **Configuration**: Update configuration settings or add new ones in [`config-manager.js`](mdc:scripts/modules/config-manager.js) and ensure getters/setters are appropriate. Update documentation in [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc) and [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc). Update the `.taskmasterconfig` structure if needed.
|
||||
6. **Documentation**: Update help text and documentation in [dev_workflow.mdc](mdc:scripts/modules/dev_workflow.mdc)
|
||||
2. **AI Integration (If Applicable)**:
|
||||
- Import necessary service functions (e.g., `generateTextService`, `streamTextService`) from [`ai-services-unified.js`](mdc:scripts/modules/ai-services-unified.js).
|
||||
- Prepare parameters (`role`, `session`, `systemPrompt`, `prompt`).
|
||||
- Call the service function.
|
||||
- Handle the response (direct text or stream object).
|
||||
- **Important**: Prefer `generateTextService` for calls sending large context (like stringified JSON) where incremental display is not needed. See [`ai_services.mdc`](mdc:.cursor/rules/ai_services.mdc) for detailed usage patterns and cautions.
|
||||
3. **UI Components**: Add any display functions to [`ui.js`](mdc:scripts/modules/ui.js) following [`ui.mdc`](mdc:.cursor/rules/ui.mdc).
|
||||
4. **Command Integration**: Add the CLI command to [`commands.js`](mdc:scripts/modules/commands.js) following [`commands.mdc`](mdc:.cursor/rules/commands.mdc).
|
||||
5. **Testing**: Write tests for all components of the feature (following [`tests.mdc`](mdc:.cursor/rules/tests.mdc))
|
||||
6. **Configuration**: Update configuration settings or add new ones in [`config-manager.js`](mdc:scripts/modules/config-manager.js) and ensure getters/setters are appropriate. Update documentation in [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc) and [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc). Update the `.taskmasterconfig` structure if needed.
|
||||
7. **Documentation**: Update help text and documentation in [`dev_workflow.mdc`](mdc:.cursor/rules/dev_workflow.mdc) and [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc).
|
||||
|
||||
## Critical Checklist for New Features
|
||||
|
||||
@@ -211,7 +217,29 @@ export {
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 2. UI COMPONENTS: Add display function to ui.js
|
||||
// 2. AI Integration: Add import and use necessary service functions
|
||||
import { generateTextService } from './ai-services-unified.js';
|
||||
|
||||
// Example usage:
|
||||
async function handleAIInteraction() {
|
||||
const role = 'user';
|
||||
const session = 'exampleSession';
|
||||
const systemPrompt = 'You are a helpful assistant.';
|
||||
const prompt = 'What is the capital of France?';
|
||||
|
||||
const result = await generateTextService(role, session, systemPrompt, prompt);
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
// Export from the module
|
||||
export {
|
||||
// ... existing exports ...
|
||||
handleAIInteraction,
|
||||
};
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 3. UI COMPONENTS: Add display function to ui.js
|
||||
/**
|
||||
* Display archive operation results
|
||||
* @param {string} archivePath - Path to the archive file
|
||||
@@ -232,7 +260,7 @@ export {
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 3. COMMAND INTEGRATION: Add to commands.js
|
||||
// 4. COMMAND INTEGRATION: Add to commands.js
|
||||
import { archiveTasks } from './task-manager.js';
|
||||
import { displayArchiveResults } from './ui.js';
|
||||
|
||||
@@ -452,7 +480,7 @@ npm test
|
||||
For each new feature:
|
||||
|
||||
1. Add help text to the command definition
|
||||
2. Update [`dev_workflow.mdc`](mdc:scripts/modules/dev_workflow.mdc) with command reference
|
||||
2. Update [`dev_workflow.mdc`](mdc:.cursor/rules/dev_workflow.mdc) with command reference
|
||||
3. Consider updating [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc) if the feature significantly changes module responsibilities.
|
||||
|
||||
Follow the existing command reference format:
|
||||
|
||||
Reference in New Issue
Block a user