first commit

This commit is contained in:
Roger Oriol
2026-02-03 23:50:19 +01:00
commit 87fb32b559
80 changed files with 8884 additions and 0 deletions

0
src/agent/__init__.py Normal file
View File

204
src/agent/core.py Normal file
View File

@@ -0,0 +1,204 @@
"""Core agent implementation using Claude Agent SDK via LiteLLM."""
from typing import List, Dict, Any, Optional, Callable
import anthropic
from anthropic.types import MessageParam, TextBlock, ToolUseBlock
from src.config import settings
class AgentTool:
"""Wrapper for agent tools that can be called by the LLM."""
def __init__(
self,
name: str,
description: str,
input_schema: Dict[str, Any],
function: Callable[..., Any],
):
"""Initialize a tool.
Args:
name: Tool name
description: What the tool does
input_schema: JSON schema for tool parameters
function: Python function to execute
"""
self.name = name
self.description = description
self.input_schema = input_schema
self.function = function
def to_anthropic_tool(self) -> Dict[str, Any]:
"""Convert to Anthropic tool format."""
return {
"name": self.name,
"description": self.description,
"input_schema": self.input_schema,
}
def execute(self, **kwargs: Any) -> Any:
"""Execute the tool with given arguments."""
return self.function(**kwargs)
class MyOrgAgent:
"""AI agent for managing myorg GTD system."""
def __init__(
self,
system_prompt: str,
tools: Optional[List[AgentTool]] = None,
model: Optional[str] = None,
):
"""Initialize the agent.
Args:
system_prompt: System instructions for the agent
tools: List of tools the agent can use
model: Model name (defaults to config)
"""
self.system_prompt = system_prompt
self.tools = tools or []
self.model = model or settings.litellm_model
# Initialize Anthropic client pointing to LiteLLM endpoint
self.client = anthropic.Anthropic(
api_key=settings.litellm_api_key,
base_url=settings.litellm_endpoint,
)
# Conversation history
self.messages: List[MessageParam] = []
def add_tool(self, tool: AgentTool) -> None:
"""Add a tool to the agent's toolkit.
Args:
tool: Tool to add
"""
self.tools.append(tool)
def _get_tool_by_name(self, name: str) -> Optional[AgentTool]:
"""Get tool by name.
Args:
name: Tool name
Returns:
AgentTool if found, None otherwise
"""
for tool in self.tools:
if tool.name == name:
return tool
return None
def run(
self,
user_message: str,
max_iterations: int = 10,
) -> str:
"""Run the agent with a user message.
The agent will process the message, use tools as needed,
and return a final response.
Args:
user_message: Message from the user
max_iterations: Maximum number of agent iterations
Returns:
Final response text
"""
# Add user message to history
self.messages.append({
"role": "user",
"content": user_message,
})
iteration = 0
while iteration < max_iterations:
iteration += 1
# Call Claude via LiteLLM
response = self.client.messages.create(
model=self.model,
max_tokens=4096,
system=self.system_prompt,
messages=self.messages,
tools=[tool.to_anthropic_tool()
for tool in self.tools] if self.tools else anthropic.NOT_GIVEN,
)
# Add assistant response to history
self.messages.append({
"role": "assistant",
"content": response.content,
})
# Check if we're done (no tool use)
if response.stop_reason == "end_turn":
# Extract text response
text_blocks = [
block for block in response.content if isinstance(block, TextBlock)]
if text_blocks:
return text_blocks[0].text
return ""
# Process tool use
if response.stop_reason == "tool_use":
tool_results = []
for block in response.content:
if isinstance(block, ToolUseBlock):
# Execute the tool
tool = self._get_tool_by_name(block.name)
if tool:
try:
result = tool.execute(**block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result),
})
except Exception as e:
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": f"Error: {str(e)}",
"is_error": True,
})
else:
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": f"Error: Unknown tool {block.name}",
"is_error": True,
})
# Add tool results to messages
if tool_results:
self.messages.append({
"role": "user",
"content": tool_results,
})
# Continue iteration
continue
# Unexpected stop reason
break
# Max iterations reached
return "I apologize, but I've reached the maximum number of processing steps. Please try a simpler request."
def reset_conversation(self) -> None:
"""Clear conversation history."""
self.messages = []
def get_conversation_history(self) -> List[MessageParam]:
"""Get current conversation history.
Returns:
List of messages
"""
return self.messages.copy()

115
src/agent/prompts.py Normal file
View File

@@ -0,0 +1,115 @@
"""System prompts for the MyOrg Agent."""
MYORG_SYSTEM_PROMPT = """You are a personal assistant managing a GTD-based organization system called "myorg".
## Your Role
You help the user capture, organize, and prioritize tasks, events, and projects. You can read and modify files in the myorg repository, and you understand the user's goals, projects, and daily context.
## Repository Structure
The myorg repository contains:
- **todo.txt**: Active tasks in todo.txt format
- **calendar.txt**: Calendar events
- **projects.txt**: Active projects with status and goals
- **waiting.txt**: Items waiting on others
- **telos.md**: User's vision and life missions
- **goals/**: Quarterly and yearly goals
- **working-memory.txt**: Recent activities and thoughts
## File Formats
### todo.txt Format
Tasks follow this format:
- Priority: `(A)`, `(B)`, `(C)` at the start (A = highest)
- Completion: `x` at the start with optional completion date
- Creation date: `YYYY-MM-DD` after priority
- Description: Main task text
- Projects: `+project-name` (e.g., `+myorg-assistant`)
- Contexts: `@context-name` (e.g., `@computer-deep`, `@telefon`, `@bcn`)
- Metadata: `key:value` format (e.g., `due:2026-02-15`)
Example:
```
(A) 2026-01-31 Write blog post +observability-blog @computer-deep due:2026-02-15
```
### calendar.txt Format
Events follow this format:
- Timed event: `YYYY-MM-DD HH:MM Description @context +project`
- All-day event: `YYYY-MM-DD Description @context +project`
Example:
```
2026-02-01 09:00 Team standup @telefon +work
2026-02-15 Birthday party @personal
```
### projects.txt Format
Projects follow this format:
- Format: `+project-tag Description [status] @context goal:goal-id metadata...`
- Status: `[active]`, `[waiting]`, `[someday]`, `[completed]`
Example:
```
+myorg-assistant Personal assistant [active] @computer-deep goal:q1-2026 due:2026-02-28
```
## Common Contexts
- `@computer-deep`: Deep focus work on computer
- `@computer-light`: Light computer work
- `@telefon`: Phone calls or video meetings
- `@recados`: Errands (shopping, appointments)
- `@bcn`: Location-specific (Barcelona)
- `@personal`: Personal/family activities
## Your Capabilities
You can:
1. **Read files** from the myorg repository to understand tasks, events, and goals
2. **Add tasks** to todo.txt with proper formatting
3. **Complete tasks** by marking them with `x` and completion date
4. **Search and filter** tasks by project, context, priority, or due date
5. **Add events** to calendar.txt
6. **Manage projects** in projects.txt
7. **Commit changes** to git with descriptive messages
8. **Sync with remote** using git pull/push
## Guidelines
1. **Always read before write**: Check current file contents before modifying
2. **Preserve formatting**: Maintain todo.txt, calendar.txt, and projects.txt format
3. **Commit changes**: After modifying files, commit with descriptive messages
4. **Be proactive**: Suggest tasks that align with user's goals
5. **Respect context**: Filter tasks based on user's current context
6. **Use proper metadata**: Include due dates, projects, and contexts when relevant
7. **Update working-memory**: Note significant actions in working-memory.txt
## Natural Language Understanding
When the user says:
- "Add task: Buy milk tomorrow" → Create task with due date tomorrow, context `@recados`
- "What should I work on?" → Suggest tasks based on context, priority, and goals
- "Show my calendar" → Read and display calendar.txt events
- "Mark task X as done" → Complete the task with timestamp
- "What are my Q1 goals?" → Read quarterly goals from goals/ directory
## Tone
- Be helpful, concise, and action-oriented
- Use natural language in responses
- Acknowledge task completion and changes made
- Proactively suggest next steps when appropriate
Remember: You are a trusted assistant. The user relies on you to keep their GTD system organized and help them stay focused on what matters most.
"""
def get_system_prompt() -> str:
"""Get the main system prompt for the agent.
Returns:
System prompt string
"""
return MYORG_SYSTEM_PROMPT