mita-code 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mita/__init__.py +5 -0
- mita/__main__.py +5 -0
- mita/agent/__init__.py +1 -0
- mita/agent/context.py +43 -0
- mita/agent/conversation.py +101 -0
- mita/agent/loop.py +594 -0
- mita/agent/system_prompt.py +75 -0
- mita/cli.py +940 -0
- mita/config/__init__.py +6 -0
- mita/config/defaults.py +41 -0
- mita/config/loader.py +53 -0
- mita/config/schema.py +131 -0
- mita/hooks/__init__.py +1 -0
- mita/hooks/manager.py +94 -0
- mita/hooks/runner.py +145 -0
- mita/index/__init__.py +1 -0
- mita/index/embeddings.py +49 -0
- mita/index/manager.py +170 -0
- mita/index/parser.py +331 -0
- mita/index/retriever.py +53 -0
- mita/index/store.py +143 -0
- mita/llm/__init__.py +1 -0
- mita/llm/client.py +86 -0
- mita/llm/instructor.py +80 -0
- mita/llm/streaming.py +58 -0
- mita/memory/__init__.py +6 -0
- mita/memory/discovery.py +61 -0
- mita/memory/loader.py +76 -0
- mita/memory/manager.py +117 -0
- mita/models/__init__.py +13 -0
- mita/models/hardware.py +289 -0
- mita/models/manager.py +268 -0
- mita/models/ollama_client.py +104 -0
- mita/models/recommender.py +88 -0
- mita/models/registry.py +167 -0
- mita/models/server.py +262 -0
- mita/plugins/__init__.py +1 -0
- mita/plugins/client.py +152 -0
- mita/plugins/manager.py +210 -0
- mita/py.typed +0 -0
- mita/skills/__init__.py +1 -0
- mita/skills/executor.py +84 -0
- mita/skills/loader.py +117 -0
- mita/skills/manager.py +129 -0
- mita/tools/__init__.py +1 -0
- mita/tools/builtins/__init__.py +28 -0
- mita/tools/builtins/file_edit.py +71 -0
- mita/tools/builtins/file_read.py +74 -0
- mita/tools/builtins/file_write.py +42 -0
- mita/tools/builtins/git.py +112 -0
- mita/tools/builtins/glob_tool.py +67 -0
- mita/tools/builtins/grep_tool.py +93 -0
- mita/tools/builtins/shell.py +83 -0
- mita/tools/executor.py +80 -0
- mita/tools/registry.py +69 -0
- mita/tools/safety.py +91 -0
- mita/tools/schema.py +87 -0
- mita/ui/__init__.py +1 -0
- mita/ui/display.py +139 -0
- mita/ui/repl.py +88 -0
- mita/ui/spinner.py +48 -0
- mita/ui/theme.py +23 -0
- mita_code-0.1.0.dist-info/METADATA +227 -0
- mita_code-0.1.0.dist-info/RECORD +67 -0
- mita_code-0.1.0.dist-info/WHEEL +4 -0
- mita_code-0.1.0.dist-info/entry_points.txt +3 -0
- mita_code-0.1.0.dist-info/licenses/LICENSE +201 -0
mita/__init__.py
ADDED
mita/__main__.py
ADDED
mita/agent/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core agent loop: prompt -> LLM -> tool calls -> execute -> loop."""
|
mita/agent/context.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Context assembly: memory + tool definitions → system prompt."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from mita.agent.conversation import Conversation, Message, Role
|
|
8
|
+
from mita.agent.system_prompt import build_system_prompt
|
|
9
|
+
from mita.config.schema import MitaConfig
|
|
10
|
+
from mita.memory.loader import load_memory
|
|
11
|
+
from mita.tools.registry import ToolRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def assemble_context(
|
|
15
|
+
conversation: Conversation,
|
|
16
|
+
config: MitaConfig,
|
|
17
|
+
registry: ToolRegistry,
|
|
18
|
+
cwd: Path | None = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Assemble the initial context for the agent loop.
|
|
21
|
+
|
|
22
|
+
Builds the system prompt from config, memory, and tool definitions,
|
|
23
|
+
and adds it to the conversation.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
conversation: The conversation to add the system message to.
|
|
27
|
+
config: Application configuration.
|
|
28
|
+
registry: Tool registry with all available tools.
|
|
29
|
+
cwd: Working directory for memory discovery. Defaults to Path.cwd().
|
|
30
|
+
"""
|
|
31
|
+
working_dir = cwd or Path.cwd()
|
|
32
|
+
|
|
33
|
+
# Load memory from MITA.md files
|
|
34
|
+
memory_content = load_memory(cwd=working_dir)
|
|
35
|
+
|
|
36
|
+
# Build system prompt
|
|
37
|
+
system_prompt = build_system_prompt(
|
|
38
|
+
config=config,
|
|
39
|
+
memory=memory_content,
|
|
40
|
+
tools=registry.get_definitions(),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
conversation.add(Message(role=Role.SYSTEM, content=system_prompt))
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Message history management with token counting and truncation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Role(StrEnum):
|
|
12
|
+
"""Message roles in the conversation."""
|
|
13
|
+
|
|
14
|
+
SYSTEM = "system"
|
|
15
|
+
USER = "user"
|
|
16
|
+
ASSISTANT = "assistant"
|
|
17
|
+
TOOL = "tool"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Message(BaseModel):
|
|
21
|
+
"""A single message in the conversation."""
|
|
22
|
+
|
|
23
|
+
role: Role
|
|
24
|
+
content: str
|
|
25
|
+
tool_calls: list[dict[str, Any]] = Field(default_factory=list)
|
|
26
|
+
tool_call_id: str | None = None # for tool-result messages
|
|
27
|
+
name: str | None = None # tool name for tool-result messages
|
|
28
|
+
|
|
29
|
+
def to_api_dict(self) -> dict[str, Any]:
|
|
30
|
+
"""Convert to LiteLLM/OpenAI API format."""
|
|
31
|
+
msg: dict[str, Any] = {"role": self.role.value, "content": self.content}
|
|
32
|
+
if self.tool_calls:
|
|
33
|
+
msg["tool_calls"] = self.tool_calls
|
|
34
|
+
if self.tool_call_id is not None:
|
|
35
|
+
msg["tool_call_id"] = self.tool_call_id
|
|
36
|
+
if self.name is not None:
|
|
37
|
+
msg["name"] = self.name
|
|
38
|
+
return msg
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Conversation(BaseModel):
|
|
42
|
+
"""Conversation history with token estimation and truncation."""
|
|
43
|
+
|
|
44
|
+
messages: list[Message] = Field(default_factory=list)
|
|
45
|
+
total_tokens: int = 0
|
|
46
|
+
|
|
47
|
+
def add(self, msg: Message) -> None:
|
|
48
|
+
"""Add a message to the conversation."""
|
|
49
|
+
self.messages.append(msg)
|
|
50
|
+
# Rough token estimate: ~4 chars per token
|
|
51
|
+
self.total_tokens += _estimate_tokens(msg.content)
|
|
52
|
+
|
|
53
|
+
def get_messages_for_api(self) -> list[dict[str, Any]]:
|
|
54
|
+
"""Convert all messages to API format."""
|
|
55
|
+
return [m.to_api_dict() for m in self.messages]
|
|
56
|
+
|
|
57
|
+
def truncate_to_fit(self, max_tokens: int) -> None:
|
|
58
|
+
"""Truncate conversation to fit within the token budget.
|
|
59
|
+
|
|
60
|
+
Strategy: Keep the system message(s) and last N messages.
|
|
61
|
+
Remove oldest non-system messages first.
|
|
62
|
+
"""
|
|
63
|
+
if self.total_tokens <= max_tokens:
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
# Separate system messages from the rest
|
|
67
|
+
system_msgs = [m for m in self.messages if m.role == Role.SYSTEM]
|
|
68
|
+
other_msgs = [m for m in self.messages if m.role != Role.SYSTEM]
|
|
69
|
+
|
|
70
|
+
system_tokens = sum(_estimate_tokens(m.content) for m in system_msgs)
|
|
71
|
+
budget = max_tokens - system_tokens
|
|
72
|
+
|
|
73
|
+
if budget <= 0:
|
|
74
|
+
# System prompt alone exceeds budget — keep just the last system msg
|
|
75
|
+
self.messages = system_msgs[-1:] if system_msgs else []
|
|
76
|
+
self.total_tokens = sum(_estimate_tokens(m.content) for m in self.messages)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
# Keep messages from the end until we hit the budget
|
|
80
|
+
kept: list[Message] = []
|
|
81
|
+
used = 0
|
|
82
|
+
for msg in reversed(other_msgs):
|
|
83
|
+
msg_tokens = _estimate_tokens(msg.content)
|
|
84
|
+
if used + msg_tokens > budget:
|
|
85
|
+
break
|
|
86
|
+
kept.append(msg)
|
|
87
|
+
used += msg_tokens
|
|
88
|
+
|
|
89
|
+
kept.reverse()
|
|
90
|
+
self.messages = system_msgs + kept
|
|
91
|
+
self.total_tokens = system_tokens + used
|
|
92
|
+
|
|
93
|
+
def clear_non_system(self) -> None:
|
|
94
|
+
"""Clear all non-system messages (for /clear command)."""
|
|
95
|
+
self.messages = [m for m in self.messages if m.role == Role.SYSTEM]
|
|
96
|
+
self.total_tokens = sum(_estimate_tokens(m.content) for m in self.messages)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _estimate_tokens(text: str) -> int:
|
|
100
|
+
"""Rough token estimate: ~4 characters per token."""
|
|
101
|
+
return max(1, len(text) // 4)
|