diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1ba74e5 --- /dev/null +++ b/.env.example @@ -0,0 +1,34 @@ +# LLM Provider Selection +# Options: "deepseek" or "ollama" +LLM_PROVIDER=ollama + +# DeepSeek LLM Configuration (if using DeepSeek) +DEEPSEEK_API_KEY=your_deepseek_api_key +DEEPSEEK_BASE_URL=https://api.deepseek.com +DEEPSEEK_MODEL=deepseek-chat + +# Ollama LLM Configuration (if using Ollama) +OLLAMA_BASE_URL=http://localhost:11434 +OLLAMA_MODEL=llama3.2 + +# LLM Settings +TEMPERATURE=0.2 + +# TMDB API Configuration +TMDB_API_KEY=your_tmdb_api_key +TMDB_BASE_URL=https://api.themoviedb.org/3 + +# Storage Configuration +MEMORY_FILE=memory.json + +# qBittorrent Configuration +QBIT_HOST=http://192.168.178.47:30024 +QBIT_USER=admin +QBIT_PASS=adminadmin + +# Security Configuration +MAX_TOOL_ITERATIONS=10 +REQUEST_TIMEOUT=30 + +# Memory Configuration +MAX_HISTORY_MESSAGES=10 diff --git a/brain/.gitignore b/.gitignore similarity index 100% rename from brain/.gitignore rename to .gitignore diff --git a/brain/infrastructure/persistence/memory.py b/brain/infrastructure/persistence/memory.py index f6fb1ae..ba7c8ff 100644 --- a/brain/infrastructure/persistence/memory.py +++ b/brain/infrastructure/persistence/memory.py @@ -496,7 +496,7 @@ class Memory: storage_dir: Directory for persistent storage """ self.storage_dir = Path(storage_dir) - self.storage_dir.mkdir(exist_ok=True) + self.storage_dir.mkdir(parents=True, exist_ok=True) self.ltm_file = self.storage_dir / "ltm.json" diff --git a/brain/tests/conftest.py b/brain/tests/conftest.py index 28d85d7..c1448e4 100644 --- a/brain/tests/conftest.py +++ b/brain/tests/conftest.py @@ -1,5 +1,11 @@ """Pytest configuration and shared fixtures.""" +import sys +from pathlib import Path + +# Ajouter le dossier parent (brain) au PYTHONPATH +sys.path.insert(0, str(Path(__file__).parent.parent)) + import shutil import tempfile from pathlib import Path diff --git a/brain/tests/test_prompts.py b/brain/tests/test_prompts.py index a62990f..28e77be 100644 --- a/brain/tests/test_prompts.py +++ b/brain/tests/test_prompts.py @@ -226,7 +226,7 @@ class TestFormatEpisodicContext: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory) assert context == "" @@ -235,7 +235,7 @@ class TestFormatEpisodicContext: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory_with_search_results) assert "LAST SEARCH" in context assert "Inception 1080p" in context @@ -249,7 +249,7 @@ class TestFormatEpisodicContext: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory) assert "LAST SEARCH" in context assert "ACTIVE DOWNLOADS" in context @@ -264,7 +264,7 @@ class TestFormatStmContext: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_stm_context() + context = builder._format_stm_context(memory) # Should at least show language assert "CONVERSATION LANGUAGE" in context or context == "" @@ -276,7 +276,7 @@ class TestFormatStmContext: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_stm_context() + context = builder._format_stm_context(memory) assert "CURRENT WORKFLOW" in context assert "download" in context @@ -290,7 +290,7 @@ class TestFormatStmContext: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_stm_context() + context = builder._format_stm_context(memory) assert "CURRENT WORKFLOW" in context assert "CURRENT TOPIC" in context diff --git a/brain/tests/test_prompts_critical.py b/brain/tests/test_prompts_critical.py index 5f5697c..3ef683c 100644 --- a/brain/tests/test_prompts_critical.py +++ b/brain/tests/test_prompts_critical.py @@ -7,7 +7,7 @@ from agent.registry import make_tools class TestPromptBuilderToolsInjection: """Critical tests for tools injection in prompts.""" - def test_system_prompt_includes_all_tools(self): + def test_system_prompt_includes_all_tools(self, memory): """CRITICAL: Verify all tools are mentioned in system prompt.""" tools = make_tools() builder = PromptBuilder(tools) @@ -19,7 +19,7 @@ class TestPromptBuilderToolsInjection: tool_name in prompt ), f"Tool {tool_name} not mentioned in system prompt" - def test_tools_spec_contains_all_registered_tools(self): + def test_tools_spec_contains_all_registered_tools(self, memory): """CRITICAL: Verify build_tools_spec() returns all tools.""" tools = make_tools() builder = PromptBuilder(tools) @@ -30,7 +30,7 @@ class TestPromptBuilderToolsInjection: assert spec_names == tool_names, f"Missing tools: {tool_names - spec_names}" - def test_tools_spec_is_not_empty(self): + def test_tools_spec_is_not_empty(self, memory): """CRITICAL: Verify tools spec is never empty.""" tools = make_tools() builder = PromptBuilder(tools) @@ -38,7 +38,7 @@ class TestPromptBuilderToolsInjection: assert len(specs) > 0, "Tools spec is empty!" - def test_tools_spec_format_matches_openai(self): + def test_tools_spec_format_matches_openai(self, memory): """CRITICAL: Verify tools spec format is OpenAI-compatible.""" tools = make_tools() builder = PromptBuilder(tools) @@ -137,7 +137,7 @@ class TestPromptBuilderMemoryContext: class TestPromptBuilderStructure: """Tests for prompt structure and completeness.""" - def test_system_prompt_is_not_empty(self): + def test_system_prompt_is_not_empty(self, memory): """Verify system prompt is never empty.""" tools = make_tools() builder = PromptBuilder(tools) @@ -146,7 +146,7 @@ class TestPromptBuilderStructure: assert len(prompt) > 0 assert prompt.strip() != "" - def test_system_prompt_includes_base_instruction(self): + def test_system_prompt_includes_base_instruction(self, memory): """Verify system prompt includes base instruction.""" tools = make_tools() builder = PromptBuilder(tools) @@ -154,7 +154,7 @@ class TestPromptBuilderStructure: assert "assistant" in prompt.lower() or "help" in prompt.lower() - def test_system_prompt_includes_rules(self): + def test_system_prompt_includes_rules(self, memory): """Verify system prompt includes important rules.""" tools = make_tools() builder = PromptBuilder(tools) @@ -162,7 +162,7 @@ class TestPromptBuilderStructure: assert "RULES" in prompt or "IMPORTANT" in prompt - def test_system_prompt_includes_examples(self): + def test_system_prompt_includes_examples(self, memory): """Verify system prompt includes examples.""" tools = make_tools() builder = PromptBuilder(tools) @@ -170,7 +170,7 @@ class TestPromptBuilderStructure: assert "EXAMPLES" in prompt or "example" in prompt.lower() - def test_tools_description_format(self): + def test_tools_description_format(self, memory): """Verify tools are properly formatted in description.""" tools = make_tools() builder = PromptBuilder(tools) @@ -188,7 +188,7 @@ class TestPromptBuilderStructure: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory_with_search_results) assert "LAST SEARCH" in context assert "Inception" in context @@ -201,7 +201,7 @@ class TestPromptBuilderStructure: memory.stm.set_topic("test_topic") memory.stm.set_entity("key", "value") - context = builder._format_stm_context() + context = builder._format_stm_context(memory) assert "TOPIC" in context or "test_topic" in context assert "ENTITIES" in context or "key" in context @@ -213,7 +213,7 @@ class TestPromptBuilderStructure: memory.ltm.set_config("test_key", "test_value") - context = builder._format_config_context() + context = builder._format_config_context(memory) assert "CONFIGURATION" in context assert "test_key" in context diff --git a/brain/tests/test_prompts_edge_cases.py b/brain/tests/test_prompts_edge_cases.py index f68cb79..5e4a3e8 100644 --- a/brain/tests/test_prompts_edge_cases.py +++ b/brain/tests/test_prompts_edge_cases.py @@ -309,7 +309,7 @@ class TestFormatEpisodicContextEdgeCases: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory) assert "LAST SEARCH" in context @@ -327,7 +327,7 @@ class TestFormatEpisodicContextEdgeCases: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory) # Should not crash assert "LAST SEARCH" in context @@ -339,7 +339,7 @@ class TestFormatEpisodicContextEdgeCases: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_episodic_context() + context = builder._format_episodic_context(memory) assert "ACTIVE DOWNLOADS" in context assert "0%" in context # Default progress @@ -358,7 +358,7 @@ class TestFormatStmContextEdgeCases: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_stm_context() + context = builder._format_stm_context(memory) assert "CURRENT WORKFLOW" in context @@ -370,7 +370,7 @@ class TestFormatStmContextEdgeCases: builder = PromptBuilder(tools) try: - context = builder._format_stm_context() + context = builder._format_stm_context(memory) assert "CURRENT WORKFLOW" in context or True except (AttributeError, TypeError): # Expected if None target causes issues @@ -383,7 +383,7 @@ class TestFormatStmContextEdgeCases: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_stm_context() + context = builder._format_stm_context(memory) # Empty topic might not be shown assert isinstance(context, str) @@ -395,6 +395,6 @@ class TestFormatStmContextEdgeCases: tools = make_tools() builder = PromptBuilder(tools) - context = builder._format_stm_context() + context = builder._format_stm_context(memory) assert "EXTRACTED ENTITIES" in context