agentkit-sdk 0.1.0__tar.gz

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.
Files changed (44) hide show
  1. agentkit_sdk-0.1.0/PKG-INFO +108 -0
  2. agentkit_sdk-0.1.0/README.md +80 -0
  3. agentkit_sdk-0.1.0/pyproject.toml +49 -0
  4. agentkit_sdk-0.1.0/setup.cfg +4 -0
  5. agentkit_sdk-0.1.0/src/agentkit/__init__.py +3 -0
  6. agentkit_sdk-0.1.0/src/agentkit/cli/__init__.py +0 -0
  7. agentkit_sdk-0.1.0/src/agentkit/cli/build.py +54 -0
  8. agentkit_sdk-0.1.0/src/agentkit/cli/deploy.py +50 -0
  9. agentkit_sdk-0.1.0/src/agentkit/cli/init.py +85 -0
  10. agentkit_sdk-0.1.0/src/agentkit/cli/main.py +22 -0
  11. agentkit_sdk-0.1.0/src/agentkit/cli/serve.py +35 -0
  12. agentkit_sdk-0.1.0/src/agentkit/core/__init__.py +3 -0
  13. agentkit_sdk-0.1.0/src/agentkit/core/context.py +75 -0
  14. agentkit_sdk-0.1.0/src/agentkit/core/pipeline.py +132 -0
  15. agentkit_sdk-0.1.0/src/agentkit/core/turn_detector.py +49 -0
  16. agentkit_sdk-0.1.0/src/agentkit/learning/__init__.py +2 -0
  17. agentkit_sdk-0.1.0/src/agentkit/learning/correction.py +109 -0
  18. agentkit_sdk-0.1.0/src/agentkit/learning/recommender.py +76 -0
  19. agentkit_sdk-0.1.0/src/agentkit/memory/__init__.py +5 -0
  20. agentkit_sdk-0.1.0/src/agentkit/memory/base.py +114 -0
  21. agentkit_sdk-0.1.0/src/agentkit/memory/extractor.py +79 -0
  22. agentkit_sdk-0.1.0/src/agentkit/memory/markdown.py +109 -0
  23. agentkit_sdk-0.1.0/src/agentkit/memory/user_model.py +1 -0
  24. agentkit_sdk-0.1.0/src/agentkit/memory/vector.py +96 -0
  25. agentkit_sdk-0.1.0/src/agentkit/providers/llm/__init__.py +5 -0
  26. agentkit_sdk-0.1.0/src/agentkit/providers/llm/base.py +36 -0
  27. agentkit_sdk-0.1.0/src/agentkit/providers/llm/gemini.py +59 -0
  28. agentkit_sdk-0.1.0/src/agentkit/providers/llm/openai.py +59 -0
  29. agentkit_sdk-0.1.0/src/agentkit/providers/stt/__init__.py +5 -0
  30. agentkit_sdk-0.1.0/src/agentkit/providers/stt/base.py +21 -0
  31. agentkit_sdk-0.1.0/src/agentkit/providers/stt/deepgram.py +53 -0
  32. agentkit_sdk-0.1.0/src/agentkit/providers/stt/sarvam.py +45 -0
  33. agentkit_sdk-0.1.0/src/agentkit/providers/tts/__init__.py +5 -0
  34. agentkit_sdk-0.1.0/src/agentkit/providers/tts/base.py +21 -0
  35. agentkit_sdk-0.1.0/src/agentkit/providers/tts/elevenlabs.py +40 -0
  36. agentkit_sdk-0.1.0/src/agentkit/providers/tts/sarvam.py +43 -0
  37. agentkit_sdk-0.1.0/src/agentkit/server/__init__.py +1 -0
  38. agentkit_sdk-0.1.0/src/agentkit/server/app.py +189 -0
  39. agentkit_sdk-0.1.0/src/agentkit_sdk.egg-info/PKG-INFO +108 -0
  40. agentkit_sdk-0.1.0/src/agentkit_sdk.egg-info/SOURCES.txt +42 -0
  41. agentkit_sdk-0.1.0/src/agentkit_sdk.egg-info/dependency_links.txt +1 -0
  42. agentkit_sdk-0.1.0/src/agentkit_sdk.egg-info/entry_points.txt +2 -0
  43. agentkit_sdk-0.1.0/src/agentkit_sdk.egg-info/requires.txt +22 -0
  44. agentkit_sdk-0.1.0/src/agentkit_sdk.egg-info/top_level.txt +1 -0
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentkit-sdk
3
+ Version: 0.1.0
4
+ Summary: Developer SDK for building personalized voice AI assistants
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: fastapi>=0.115.0
8
+ Requires-Dist: uvicorn[standard]>=0.32.0
9
+ Requires-Dist: websockets>=14.0
10
+ Requires-Dist: pydantic>=2.10.0
11
+ Requires-Dist: pydantic-settings>=2.6.0
12
+ Requires-Dist: python-dotenv>=1.0.0
13
+ Requires-Dist: pyyaml>=6.0.0
14
+ Requires-Dist: click>=8.1.0
15
+ Requires-Dist: httpx>=0.27.0
16
+ Requires-Dist: google-generativeai>=0.8.0
17
+ Requires-Dist: openai>=1.50.0
18
+ Requires-Dist: qdrant-client>=1.12.0
19
+ Requires-Dist: numpy>=1.26.0
20
+ Requires-Dist: tiktoken>=0.7.0
21
+ Requires-Dist: aiofiles>=24.0.0
22
+ Requires-Dist: tenacity>=9.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
25
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.8.0; extra == "dev"
27
+ Requires-Dist: mypy>=1.13.0; extra == "dev"
28
+
29
+ # AgentKit
30
+
31
+ Developer SDK for building personalized voice AI assistants with mobile APK generation.
32
+
33
+ ## Quick Start
34
+
35
+ ```bash
36
+ # Install
37
+ pip install agentkit
38
+
39
+ # Initialize a new agent project
40
+ agentkit init my-agent
41
+
42
+ # Add API keys to .env
43
+ cd my-agent
44
+ cp .env.example .env
45
+
46
+ # Start the server
47
+ agentkit serve
48
+ ```
49
+
50
+ ## Configuration
51
+
52
+ Edit `agent.config.yaml`:
53
+
54
+ ```yaml
55
+ agent:
56
+ name: "Assistant"
57
+ persona: "You are a helpful personal assistant..."
58
+ language: hinglish
59
+
60
+ voice:
61
+ stt:
62
+ provider: sarvam # or deepgram
63
+ api_key: ${SARVAM_API_KEY}
64
+ tts:
65
+ provider: sarvam # or elevenlabs
66
+ voice: "meera"
67
+ api_key: ${SARVAM_API_KEY}
68
+
69
+ llm:
70
+ provider: gemini # or openai
71
+ model: gemini-2.0-flash
72
+ api_key: ${GEMINI_API_KEY}
73
+ temperature: 0.7
74
+
75
+ memory:
76
+ type: markdown # or vector
77
+ backend: local # or qdrant
78
+
79
+ learning:
80
+ enabled: true
81
+ correction_detection: true
82
+ implicit_feedback: true
83
+ ```
84
+
85
+ ## Commands
86
+
87
+ - `agentkit init` - Initialize a new project
88
+ - `agentkit serve` - Start the FastAPI server
89
+ - `agentkit build android` - Build Android APK
90
+ - `agentkit deploy --platform railway` - Deploy to Railway or Render
91
+
92
+ ## Architecture
93
+
94
+ - **CLI** - Python Click commands for init, serve, build, deploy
95
+ - **Pipeline** - Streaming STT → LLM → TTS orchestration with <500ms latency
96
+ - **Memory** - Markdown (MVP) or Qdrant vector storage with semantic retrieval
97
+ - **Learning** - Explicit/implicit correction detection, proactive recommendations
98
+ - **Mobile** - React Native + Expo VoiceOrb interface
99
+
100
+ ## Environment Variables
101
+
102
+ ```
103
+ SARVAM_API_KEY=your_sarvam_key
104
+ GEMINI_API_KEY=your_gemini_key
105
+ DEEPGRAM_API_KEY=your_deepgram_key
106
+ ELEVENLABS_API_KEY=your_elevenlabs_key
107
+ OPENAI_API_KEY=your_openai_key
108
+ ```
@@ -0,0 +1,80 @@
1
+ # AgentKit
2
+
3
+ Developer SDK for building personalized voice AI assistants with mobile APK generation.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Install
9
+ pip install agentkit
10
+
11
+ # Initialize a new agent project
12
+ agentkit init my-agent
13
+
14
+ # Add API keys to .env
15
+ cd my-agent
16
+ cp .env.example .env
17
+
18
+ # Start the server
19
+ agentkit serve
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ Edit `agent.config.yaml`:
25
+
26
+ ```yaml
27
+ agent:
28
+ name: "Assistant"
29
+ persona: "You are a helpful personal assistant..."
30
+ language: hinglish
31
+
32
+ voice:
33
+ stt:
34
+ provider: sarvam # or deepgram
35
+ api_key: ${SARVAM_API_KEY}
36
+ tts:
37
+ provider: sarvam # or elevenlabs
38
+ voice: "meera"
39
+ api_key: ${SARVAM_API_KEY}
40
+
41
+ llm:
42
+ provider: gemini # or openai
43
+ model: gemini-2.0-flash
44
+ api_key: ${GEMINI_API_KEY}
45
+ temperature: 0.7
46
+
47
+ memory:
48
+ type: markdown # or vector
49
+ backend: local # or qdrant
50
+
51
+ learning:
52
+ enabled: true
53
+ correction_detection: true
54
+ implicit_feedback: true
55
+ ```
56
+
57
+ ## Commands
58
+
59
+ - `agentkit init` - Initialize a new project
60
+ - `agentkit serve` - Start the FastAPI server
61
+ - `agentkit build android` - Build Android APK
62
+ - `agentkit deploy --platform railway` - Deploy to Railway or Render
63
+
64
+ ## Architecture
65
+
66
+ - **CLI** - Python Click commands for init, serve, build, deploy
67
+ - **Pipeline** - Streaming STT → LLM → TTS orchestration with <500ms latency
68
+ - **Memory** - Markdown (MVP) or Qdrant vector storage with semantic retrieval
69
+ - **Learning** - Explicit/implicit correction detection, proactive recommendations
70
+ - **Mobile** - React Native + Expo VoiceOrb interface
71
+
72
+ ## Environment Variables
73
+
74
+ ```
75
+ SARVAM_API_KEY=your_sarvam_key
76
+ GEMINI_API_KEY=your_gemini_key
77
+ DEEPGRAM_API_KEY=your_deepgram_key
78
+ ELEVENLABS_API_KEY=your_elevenlabs_key
79
+ OPENAI_API_KEY=your_openai_key
80
+ ```
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agentkit-sdk"
7
+ version = "0.1.0"
8
+ description = "Developer SDK for building personalized voice AI assistants"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = [
12
+ "fastapi>=0.115.0",
13
+ "uvicorn[standard]>=0.32.0",
14
+ "websockets>=14.0",
15
+ "pydantic>=2.10.0",
16
+ "pydantic-settings>=2.6.0",
17
+ "python-dotenv>=1.0.0",
18
+ "pyyaml>=6.0.0",
19
+ "click>=8.1.0",
20
+ "httpx>=0.27.0",
21
+ "google-generativeai>=0.8.0",
22
+ "openai>=1.50.0",
23
+ "qdrant-client>=1.12.0",
24
+ "numpy>=1.26.0",
25
+ "tiktoken>=0.7.0",
26
+ "aiofiles>=24.0.0",
27
+ "tenacity>=9.0.0",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=8.0.0",
33
+ "pytest-asyncio>=0.24.0",
34
+ "ruff>=0.8.0",
35
+ "mypy>=1.13.0",
36
+ ]
37
+
38
+ [project.scripts]
39
+ agentkit = "agentkit.cli.main:cli"
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+ include = ["agentkit", "agentkit.cli", "agentkit.core", "agentkit.memory", "agentkit.learning", "agentkit.providers.*", "agentkit.server"]
44
+
45
+ [tool.ruff]
46
+ target-version = "py311"
47
+
48
+ [tool.mypy]
49
+ python-version = "3.11"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """AgentKit - Developer SDK for building personalized voice AI assistants"""
2
+
3
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,54 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+
10
+ @click.command()
11
+ @click.option("--config", default="agent.config.yaml", help="Config file path")
12
+ @click.option("--output", default="build/", help="Output directory")
13
+ def android(config: str, output: str):
14
+ """Build Android APK from AgentShell template."""
15
+
16
+ if not Path(config).exists():
17
+ click.echo(f"Error: Config file '{config}' not found.")
18
+ sys.exit(1)
19
+
20
+ click.echo("Building Android APK...")
21
+
22
+ import yaml
23
+ with open(config) as f:
24
+ cfg = yaml.safe_load(f)
25
+
26
+ agent_name = cfg.get("agent", {}).get("name", "Agent")
27
+ backend_url = cfg.get("deployment", {}).get("backend_url", "ws://localhost:8000/ws/voice")
28
+
29
+ template_dir = Path(__file__).parent.parent / "mobile" / "AgentShell"
30
+
31
+ if not template_dir.exists():
32
+ click.echo("Error: AgentShell template not found.")
33
+ sys.exit(1)
34
+
35
+ project_dir = Path(output)
36
+ project_dir.mkdir(parents=True, exist_ok=True)
37
+
38
+ shutil.copytree(template_dir, project_dir / "AgentShell", dirs_exist_ok=True)
39
+
40
+ app_js = project_dir / "AgentShell" / "App.jsx"
41
+ if app_js.exists():
42
+ content = app_js.read_text()
43
+ content = content.replace("ws://localhost:8000/ws/voice", backend_url)
44
+ content = content.replace("Assistant", agent_name)
45
+ app_js.write_text(content)
46
+
47
+ click.echo("Installing dependencies...")
48
+ os.chdir(project_dir / "AgentShell")
49
+ subprocess.run(["npm", "install"], check=True)
50
+
51
+ click.echo("Building Android APK...")
52
+ subprocess.run(["npx", "expo", "run:android"], check=True)
53
+
54
+ click.echo(f"APK built at: {project_dir / "AgentShell/android/app/build/outputs/apk/release/"}")
@@ -0,0 +1,50 @@
1
+ import subprocess
2
+ import sys
3
+
4
+ import click
5
+
6
+
7
+ PLATFORMS = ["railway", "render"]
8
+
9
+
10
+ @click.command()
11
+ @click.option("--platform", type=click.Choice(PLATFORMS), required=True, help="Deployment platform")
12
+ @click.option("--config", default="agent.config.yaml", help="Config file path")
13
+ def deploy(platform: str, config: str):
14
+ """Deploy backend to Railway or Render."""
15
+
16
+ click.echo(f"Deploying to {platform}...")
17
+
18
+ if platform == "railway":
19
+ _deploy_railway()
20
+ elif platform == "render":
21
+ _deploy_render()
22
+
23
+
24
+ def _deploy_railway():
25
+ try:
26
+ result = subprocess.run(["railway", "login"], capture_output=True)
27
+ if result.returncode != 0:
28
+ click.echo("Please install Railway CLI: npm install -g @railway/cli")
29
+ sys.exit(1)
30
+
31
+ subprocess.run(["railway", "init"], check=True)
32
+ subprocess.run(["railway", "up"], check=True)
33
+
34
+ click.echo("Deployed to Railway! Run 'railway link' to connect.")
35
+ except FileNotFoundError:
36
+ click.echo("Railway CLI not found. Install with: npm install -g @railway/cli")
37
+
38
+
39
+ def _deploy_render():
40
+ try:
41
+ result = subprocess.run(["render", "--version"], capture_output=True)
42
+ if result.returncode != 0:
43
+ click.echo("Please install Render CLI: npm install -g render-cli")
44
+ sys.exit(1)
45
+
46
+ subprocess.run(["render", "blueprint", "init"], check=True)
47
+
48
+ click.echo("Deployed to Render! Check your dashboard for the live URL.")
49
+ except FileNotFoundError:
50
+ click.echo("Render CLI not found. Install with: npm install -g render-cli")
@@ -0,0 +1,85 @@
1
+ import os
2
+ import shutil
3
+ from pathlib import Path
4
+
5
+ import click
6
+ import yaml
7
+
8
+
9
+ @click.command()
10
+ @click.option("--name", default="my-agent", help="Agent name")
11
+ @click.option("--output-dir", default=".", help="Output directory")
12
+ def init(name: str, output_dir: str):
13
+ """Initialize a new AgentKit project."""
14
+
15
+ project_dir = Path(output_dir) / name
16
+ project_dir.mkdir(parents=True, exist_ok=True)
17
+
18
+ config_content = {
19
+ "agent": {
20
+ "name": name,
21
+ "persona": "You are a helpful personal assistant that remembers conversations and learns from corrections.",
22
+ "language": "hinglish"
23
+ },
24
+ "voice": {
25
+ "enabled": True,
26
+ "stt": {
27
+ "provider": "sarvam",
28
+ "api_key": "${SARVAM_API_KEY}"
29
+ },
30
+ "tts": {
31
+ "provider": "sarvam",
32
+ "voice": "meera",
33
+ "api_key": "${SARVAM_API_KEY}"
34
+ }
35
+ },
36
+ "llm": {
37
+ "provider": "gemini",
38
+ "model": "gemini-2.0-flash",
39
+ "api_key": "${GEMINI_API_KEY}",
40
+ "temperature": 0.7
41
+ },
42
+ "memory": {
43
+ "type": "markdown",
44
+ "backend": "local",
45
+ "episodic_window": 20,
46
+ "semantic_top_k": 5
47
+ },
48
+ "learning": {
49
+ "enabled": True,
50
+ "correction_detection": True,
51
+ "implicit_feedback": True,
52
+ "profile_extraction": True
53
+ },
54
+ "deployment": {
55
+ "type": "self-host",
56
+ "port": 8000
57
+ }
58
+ }
59
+
60
+ config_path = project_dir / "agent.config.yaml"
61
+ with open(config_path, "w") as f:
62
+ yaml.dump(config_content, f, default_flow_style=False)
63
+
64
+ env_example = """# API Keys
65
+ SARVAM_API_KEY=your_sarvam_key
66
+ GEMINI_API_KEY=your_gemini_key
67
+ DEEPGRAM_API_KEY=your_deepgram_key
68
+ ELEVENLABS_API_KEY=your_elevenlabs_key
69
+ OPENAI_API_KEY=your_openai_key
70
+ """
71
+
72
+ env_path = project_dir / ".env.example"
73
+ with open(env_path, "w") as f:
74
+ f.write(env_example)
75
+
76
+ src_dir = project_dir / "src"
77
+ src_dir.mkdir(exist_ok=True)
78
+
79
+ (src_dir / "__init__.py").touch()
80
+
81
+ click.echo(f"AgentKit project '{name}' created at {project_dir}")
82
+ click.echo(f"Next steps:")
83
+ click.echo(f" 1. cd {project_dir}")
84
+ click.echo(f" 2. cp .env.example .env and add your API keys")
85
+ click.echo(f" 3. agentkit serve")
@@ -0,0 +1,22 @@
1
+ import click
2
+
3
+ from agentkit.cli.init import init
4
+ from agentkit.cli.serve import serve
5
+ from agentkit.cli.build import android
6
+ from agentkit.cli.deploy import deploy
7
+
8
+
9
+ @click.group()
10
+ def cli():
11
+ """AgentKit - Build personalized voice AI assistants"""
12
+ pass
13
+
14
+
15
+ cli.add_command(init)
16
+ cli.add_command(serve)
17
+ cli.add_command(android)
18
+ cli.add_command(deploy)
19
+
20
+
21
+ if __name__ == "__main__":
22
+ cli()
@@ -0,0 +1,35 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import click
6
+ from dotenv import load_dotenv
7
+
8
+
9
+ @click.command()
10
+ @click.option("--config", default="agent.config.yaml", help="Config file path")
11
+ @click.option("--port", default=8000, help="Server port")
12
+ @click.option("--reload", is_flag=True, help="Enable auto-reload")
13
+ def serve(config: str, port: int, reload: bool):
14
+ """Start the AgentKit server."""
15
+
16
+ if not Path(config).exists():
17
+ click.echo(f"Error: Config file '{config}' not found.")
18
+ click.echo("Run 'agentkit init' first to create a project.")
19
+ sys.exit(1)
20
+
21
+ load_dotenv()
22
+
23
+ import uvicorn
24
+ from agentkit.server.app import app
25
+
26
+ click.echo(f"Starting AgentKit server on port {port}...")
27
+ click.echo(f"Playground available at http://localhost:{port}/playground")
28
+
29
+ uvicorn.run(
30
+ "agentkit.server.app:app",
31
+ host="0.0.0.0",
32
+ port=port,
33
+ reload=reload,
34
+ env_file=".env",
35
+ )
@@ -0,0 +1,3 @@
1
+ from .pipeline import VoicePipeline, PipelineConfig, Turn
2
+ from .context import ContextAssembler, MemoryContext
3
+ from .turn_detector import TurnDetector
@@ -0,0 +1,75 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class MemoryContext:
6
+ user_profile_summary: str = ""
7
+ semantic_episodes: str = ""
8
+ correction_rules: str = ""
9
+ recent_turns: str = ""
10
+
11
+ def assemble(self) -> str:
12
+ parts = []
13
+ if self.user_profile_summary:
14
+ parts.append(f"User Profile:\n{self.user_profile_summary}")
15
+ if self.semantic_episodes:
16
+ parts.append(f"Relevant History:\n{self.semantic_episodes}")
17
+ if self.correction_rules:
18
+ parts.append(f"Correction Rules:\n{self.correction_rules}")
19
+ if self.recent_turns:
20
+ parts.append(f"Recent Conversation:\n{self.recent_turns}")
21
+ return "\n\n".join(parts)
22
+
23
+
24
+ class ContextAssembler:
25
+ def __init__(self, memory):
26
+ self.memory = memory
27
+
28
+ async def assemble(self, query: str, user_id: str) -> MemoryContext:
29
+ context = MemoryContext()
30
+
31
+ user_model = await self.memory.get_user_model(user_id)
32
+ if user_model:
33
+ context.user_profile_summary = self._summarize_user_model(user_model)
34
+
35
+ semantic_results = await self.memory.retrieve(query, user_id)
36
+ if semantic_results:
37
+ context.semantic_episodes = self._format_semantic_results(semantic_results)
38
+
39
+ if user_model and user_model.correction_rules:
40
+ context.correction_rules = self._format_corrections(user_model.correction_rules)
41
+
42
+ recent_turns = await self.memory.get_recent_turns(user_id, limit=20)
43
+ if recent_turns:
44
+ context.recent_turns = self._format_recent_turns(recent_turns)
45
+
46
+ return context
47
+
48
+ def _summarize_user_model(self, user_model) -> str:
49
+ parts = [f"Name: {user_model.name}"]
50
+ if user_model.communication_style:
51
+ parts.append(f"Style: {user_model.communication_style}")
52
+ if user_model.inferred_interests:
53
+ parts.append(f"Interests: {', '.join(user_model.inferred_interests[:5])}")
54
+ if user_model.stated_goals:
55
+ parts.append(f"Goals: {', '.join(user_model.stated_goals[:3])}")
56
+ return "\n".join(parts)
57
+
58
+ def _format_semantic_results(self, results: list) -> str:
59
+ formatted = []
60
+ for r in results[:5]:
61
+ formatted.append(f"- {r.get('content', '')[:200]}")
62
+ return "\n".join(formatted)
63
+
64
+ def _format_corrections(self, corrections: list) -> str:
65
+ formatted = []
66
+ for c in corrections[:10]:
67
+ formatted.append(f"- {c.get('rule', '')}")
68
+ return "\n".join(formatted)
69
+
70
+ def _format_recent_turns(self, turns: list) -> str:
71
+ formatted = []
72
+ for turn in turns:
73
+ formatted.append(f"User: {turn.get('user_message', '')}")
74
+ formatted.append(f"Assistant: {turn.get('assistant_message', '')}")
75
+ return "\n".join(formatted[-40:])