diff --git a/apps/inference/src/claudebench_inference/prompts.py b/apps/inference/src/claudebench_inference/prompts.py index f01ae16..fc50025 100644 --- a/apps/inference/src/claudebench_inference/prompts.py +++ b/apps/inference/src/claudebench_inference/prompts.py @@ -79,13 +79,16 @@ def build_context_prompt(self, subtaskId: str, specialist: str, subtask: Dict[st description = subtask.get('description', 'No description provided') dependencies = subtask.get('dependencies', []) constraints = subtask.get('context', {}).get('constraints', []) + # Extract attachments from subtask data + attachments = subtask.get('attachments', []) return template.render( subtaskId=subtaskId, specialist=specialist, description=description, dependencies=dependencies, - constraints=constraints + constraints=constraints, + attachments=attachments ) def build_conflict_prompt(self, solutions: List[ConflictSolution], context: ConflictContext) -> str: diff --git a/apps/inference/src/claudebench_inference/templates/specialist-context.j2 b/apps/inference/src/claudebench_inference/templates/specialist-context.j2 index b30a248..adab02a 100644 --- a/apps/inference/src/claudebench_inference/templates/specialist-context.j2 +++ b/apps/inference/src/claudebench_inference/templates/specialist-context.j2 @@ -14,6 +14,32 @@ PROJECT CONSTRAINTS: {% endfor %} {% endif %} +{% if attachments and attachments|length > 0 %} +TASK ATTACHMENTS (Important Context): +{% for attachment in attachments %} +- {{ attachment.key }} ({{ attachment.type }}): + {% if attachment.type == 'json' and attachment.value %} + {% if attachment.key == 'result' or 'test-results' in attachment.key %} + Previous Results: {{ attachment.value | tojson(indent=2) }} + {% elif 'git-commit-' in attachment.key %} + Git Commit Info: {{ attachment.value | tojson(indent=2) }} + {% elif 'context_' in attachment.key %} + Previous Context: {{ attachment.value | tojson(indent=2) }} + {% elif attachment.key == 'error-details' %} + Error Information: {{ attachment.value | tojson(indent=2) }} + {% else %} + Content: {{ attachment.value | tojson(indent=2) }} + {% endif %} + {% elif attachment.type == 'markdown' and attachment.content %} + Content: {{ attachment.content }} + {% elif attachment.type == 'text' and attachment.content %} + Content: {{ attachment.content }} + {% elif attachment.type == 'url' and attachment.url %} + URL: {{ attachment.url }} + {% endif %} +{% endfor %} +{% endif %} + IMPORTANT: You have a LIMITED number of turns to complete your exploration and provide the JSON response. EFFICIENCY TIP: Use multiple tools in parallel to maximize efficiency: - Read multiple files in a single turn (e.g., Read file1, file2, file3 simultaneously) diff --git a/apps/server/src/handlers/task/task.context.handler.ts b/apps/server/src/handlers/task/task.context.handler.ts index 8f6017f..27c0a61 100644 --- a/apps/server/src/handlers/task/task.context.handler.ts +++ b/apps/server/src/handlers/task/task.context.handler.ts @@ -89,6 +89,9 @@ export class TaskContextHandler { const taskKey = `cb:task:${input.taskId}`; const taskData = await redis.pub.hgetall(taskKey); + // Store attachments separately since they come from DB + let taskAttachments: any[] = []; + if (!taskData || Object.keys(taskData).length === 0) { // Try to fetch from database if (ctx.prisma) { @@ -96,8 +99,8 @@ export class TaskContextHandler { where: { id: input.taskId }, include: { attachments: { - where: { type: "json" }, - take: 5 + orderBy: { createdAt: 'desc' }, + take: 10 // Increased from 5 to get more context } } }); @@ -106,6 +109,9 @@ export class TaskContextHandler { throw new Error(`Task ${input.taskId} not found`); } + // Store attachments for later use + taskAttachments = task.attachments || []; + // Use database data taskData.text = task.text; taskData.status = task.status; @@ -115,8 +121,33 @@ export class TaskContextHandler { } else { throw new Error(`Task ${input.taskId} not found`); } + } else { + // Task found in Redis, but we still need to fetch attachments from DB + if (ctx.prisma) { + const attachments = await ctx.prisma.taskAttachment.findMany({ + where: { taskId: input.taskId }, + orderBy: { createdAt: 'desc' }, + take: 10 + }); + taskAttachments = attachments || []; + } } + // Process attachments to extract meaningful content + const processedAttachments = taskAttachments.map(att => ({ + key: att.key, + type: att.type, + createdAt: att.createdAt, + // Parse JSON values for json type attachments + value: att.type === 'json' && att.value ? + (typeof att.value === 'string' ? JSON.parse(att.value) : att.value) : + att.value, + // Include content for text/markdown attachments + content: att.content || null, + // Include URL for url type attachments + url: att.url || null + })); + // Prepare task info for context generation const taskInfo = { id: input.taskId, @@ -125,6 +156,7 @@ export class TaskContextHandler { status: taskData.status, priority: parseInt(taskData.priority || "50"), metadata: taskData.metadata ? JSON.parse(taskData.metadata) : {}, + attachments: processedAttachments, // Include processed attachments constraints: input.constraints || [], requirements: input.requirements || [], existingFiles: input.existingFiles || [], @@ -164,6 +196,7 @@ export class TaskContextHandler { // Generate the prompt for the specialist const contextData = { ...response, + attachments: processedAttachments, // Add attachments to context customConstraints: input.constraints, customRequirements: input.requirements, existingFiles: input.existingFiles, diff --git a/apps/server/src/handlers/task/task.get_project.handler.ts b/apps/server/src/handlers/task/task.get_project.handler.ts index 7949b58..3bf7aae 100644 --- a/apps/server/src/handlers/task/task.get_project.handler.ts +++ b/apps/server/src/handlers/task/task.get_project.handler.ts @@ -78,13 +78,24 @@ export class TaskGetProjectHandler { throw new Error(`Parent task ${parentTaskId} not found`); } - // Fetch all subtasks for this project + // Fetch all subtasks for this project (excluding the parent task) const subtasks = await ctx.prisma.task.findMany({ where: { - metadata: { - path: ["projectId"], - equals: projectId - } + AND: [ + { + metadata: { + path: ["projectId"], + equals: projectId + } + }, + { + // Only include actual subtasks - they have type: "subtask" + metadata: { + path: ["type"], + equals: "subtask" + } + } + ] }, include: { attachments: { @@ -99,8 +110,8 @@ export class TaskGetProjectHandler { orderBy: { createdAt: "asc" } }); - // Filter out the parent task from subtasks - const actualSubtasks = subtasks.filter(t => t.id !== parentTaskId); + // Subtasks are already filtered by type + const actualSubtasks = subtasks; // Get project metadata from Redis cache or attachments const projectKey = `cb:project:${projectId}`; diff --git a/apps/server/src/templates/task/task-context-prompt.njk b/apps/server/src/templates/task/task-context-prompt.njk index 0f35021..872da0d 100644 --- a/apps/server/src/templates/task/task-context-prompt.njk +++ b/apps/server/src/templates/task/task-context-prompt.njk @@ -47,6 +47,55 @@ You must review these files before implementation: {% endfor %} {% endif %} +{% if attachments and attachments.length > 0 %} +## Task Attachments +The following attachments provide important context for this task: +{% for attachment in attachments %} + +### {{ attachment.key }} +- **Type:** {{ attachment.type }} +- **Created:** {{ attachment.createdAt }} +{% if attachment.type == "json" and attachment.value %} +{% if attachment.key == "result" or attachment.key.startsWith("test-results") %} +**Previous Results:** +```json +{{ attachment.value | dump(2) }} +``` +{% elif attachment.key.startsWith("git-commit-") %} +**Git Commit Information:** +```json +{{ attachment.value | dump(2) }} +``` +{% elif attachment.key.startsWith("context_") %} +**Previous Context Generation:** +```json +{{ attachment.value | dump(2) }} +``` +{% elif attachment.key == "error-details" %} +**Error Information:** +```json +{{ attachment.value | dump(2) }} +``` +{% else %} +**Content:** +```json +{{ attachment.value | dump(2) }} +``` +{% endif %} +{% elif attachment.type == "markdown" and attachment.content %} +**Content:** +{{ attachment.content }} +{% elif attachment.type == "text" and attachment.content %} +**Content:** +``` +{{ attachment.content }} +``` +{% elif attachment.type == "url" and attachment.url %} +**URL:** {{ attachment.url }} +{% endif %} +{% endfor %} +{% endif %} + {% if relatedWork and relatedWork.length > 0 %} ## Related Work Other work in progress that may affect your implementation: