codebuff-agent 2.1.74__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.
- codebuff_agent-2.1.74.data/data/etc/jupyter/jupyter_server_config.d/codebuff_agent.json +7 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/package.json +157 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/104.2b8f20f9d1ac2c9db01b.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/150.39fa98117cc8ca590465.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/166.386ff9b2c37ac865fdee.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/216.25cf81131bf7274dabf4.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/353.581bd1cb00746399b62e.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/367.575f9621b63c9a641aa9.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/421.11dac223a2c2da1b09f5.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/432.8513b4c6cc1d1232d165.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/432.8513b4c6cc1d1232d165.js.LICENSE.txt +19 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/439.ba5b5ef70ec6de214b8c.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/439.ba5b5ef70ec6de214b8c.js.LICENSE.txt +8 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/481.cc2dec9fa869d66f1407.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/487.0796e931d6e651482c14.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/49.d326f554b6f9ccde6580.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/541.26897ea32845659e36be.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/579.e569d3edd8391fd8fbb3.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/58.eb7cc679f7535cc8a2f1.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/58.eb7cc679f7535cc8a2f1.js.LICENSE.txt +8 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/666.d9bfdb23f98489d0a188.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/677.d266c39dbed33bc388b3.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/677.d266c39dbed33bc388b3.js.LICENSE.txt +8 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/68.26b6f59c8454adb17f20.js +204 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/694.4ac443814260057e68f7.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/791.61affb8d3480d3d60e0d.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/848.4d2db09cfbb09374b299.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/848.4d2db09cfbb09374b299.js.LICENSE.txt +9 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/868.5cfe7e1a348ec5af1b8c.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/97.ea559a5b8a9a09f17c6b.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/remoteEntry.5292fc9c6df7e7920cc8.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/style.js +4 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/third-party-licenses.json +724 -0
- codebuff_agent-2.1.74.dist-info/METADATA +142 -0
- codebuff_agent-2.1.74.dist-info/RECORD +217 -0
- codebuff_agent-2.1.74.dist-info/WHEEL +4 -0
- codebuff_agent-2.1.74.dist-info/licenses/LICENSE +21 -0
- codebuff_core/__init__.py +153 -0
- codebuff_core/codebuff_config_defaults.json +64 -0
- codebuff_core/core/__init__.py +35 -0
- codebuff_core/core/code_validator.py +1238 -0
- codebuff_core/core/error_classifier.py +579 -0
- codebuff_core/core/exceptions.py +49 -0
- codebuff_core/data/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +100256 -0
- codebuff_core/directory_policy.py +32 -0
- codebuff_core/factory.py +155 -0
- codebuff_core/interfaces.py +197 -0
- codebuff_core/knowledge/__init__.py +31 -0
- codebuff_core/knowledge/chunking.py +375 -0
- codebuff_core/knowledge/loader.py +339 -0
- codebuff_core/knowledge/watchdog_service.py +354 -0
- codebuff_core/legacy_paths.py +69 -0
- codebuff_core/llm/__init__.py +13 -0
- codebuff_core/llm/service.py +407 -0
- codebuff_core/managers/__init__.py +22 -0
- codebuff_core/managers/config_manager.py +690 -0
- codebuff_core/managers/remote_config.py +673 -0
- codebuff_core/managers/remote_skills.py +539 -0
- codebuff_core/managers/session_manager.py +255 -0
- codebuff_core/middleware/__init__.py +3 -0
- codebuff_core/middleware/error_recovery.py +394 -0
- codebuff_core/middleware/logging_utils.py +33 -0
- codebuff_core/models/__init__.py +111 -0
- codebuff_core/models/agent.py +306 -0
- codebuff_core/models/chat.py +57 -0
- codebuff_core/models/common.py +82 -0
- codebuff_core/models/rag.py +300 -0
- codebuff_core/platform_rules.py +127 -0
- codebuff_core/prompts/__init__.py +44 -0
- codebuff_core/prompts/auto_agent_prompts.py +1278 -0
- codebuff_core/services/__init__.py +30 -0
- codebuff_core/services/agent_service.py +345 -0
- codebuff_core/services/analytics_aggregate.py +837 -0
- codebuff_core/services/chat_service.py +898 -0
- codebuff_core/services/context_condenser.py +306 -0
- codebuff_core/services/pii_guardrail.py +393 -0
- codebuff_core/services/rag_chat_agent.py +2993 -0
- codebuff_core/services/rag_collections.py +2740 -0
- codebuff_core/services/rag_manager.py +890 -0
- codebuff_core/services/rag_service.py +145 -0
- codebuff_core/services/retriever.py +260 -0
- codebuff_core/services/vllm_embedding_service.py +290 -0
- codebuff_core/tests/__init__.py +1 -0
- codebuff_core/tests/conftest.py +81 -0
- codebuff_core/tests/test_error_recovery.py +647 -0
- codebuff_core/tests/test_factory.py +135 -0
- codebuff_core/tests/test_services.py +150 -0
- jupyter_ext/__init__.py +266 -0
- jupyter_ext/_version.py +4 -0
- jupyter_ext/agent/__init__.py +49 -0
- jupyter_ext/agent/agent_factory.py +2270 -0
- jupyter_ext/agent/ast_tools.py +451 -0
- jupyter_ext/agent/autofix_middleware.py +481 -0
- jupyter_ext/agent/background_procs.py +372 -0
- jupyter_ext/agent/code_history_middleware.py +211 -0
- jupyter_ext/agent/codex_autofix.py +160 -0
- jupyter_ext/agent/codex_bridge.py +3339 -0
- jupyter_ext/agent/codex_hooks.py +320 -0
- jupyter_ext/agent/codex_slash.py +685 -0
- jupyter_ext/agent/codex_threads.py +114 -0
- jupyter_ext/agent/content_injection.py +377 -0
- jupyter_ext/agent/continuation_middleware.py +548 -0
- jupyter_ext/agent/custom_skills.py +335 -0
- jupyter_ext/agent/file_diffs.py +99 -0
- jupyter_ext/agent/git_history.py +188 -0
- jupyter_ext/agent/guardrail_middleware.py +421 -0
- jupyter_ext/agent/json_repair_utils.py +54 -0
- jupyter_ext/agent/langchain_tools.py +2673 -0
- jupyter_ext/agent/mermaid_fixer.py +289 -0
- jupyter_ext/agent/mermaid_middleware.py +135 -0
- jupyter_ext/agent/middleware.py +359 -0
- jupyter_ext/agent/middleware_prompts.py +53 -0
- jupyter_ext/agent/observability.py +334 -0
- jupyter_ext/agent/patch_tool_calls.py +57 -0
- jupyter_ext/agent/prompts.py +112 -0
- jupyter_ext/agent/skill_middleware.py +257 -0
- jupyter_ext/agent/skills/airflow_dag_development.md +36 -0
- jupyter_ext/agent/skills/anomaly_detection.md +12 -0
- jupyter_ext/agent/skills/athena_sql.md +156 -0
- jupyter_ext/agent/skills/bdp_table_search.md +33 -0
- jupyter_ext/agent/skills/bigquery.md +65 -0
- jupyter_ext/agent/skills/bigquery_oracle.md +231 -0
- jupyter_ext/agent/skills/bigquery_tuning.md +307 -0
- jupyter_ext/agent/skills/classification_evaluation.md +11 -0
- jupyter_ext/agent/skills/clustering_analysis.md +12 -0
- jupyter_ext/agent/skills/comprehensive_eda.md +11 -0
- jupyter_ext/agent/skills/data_analysis.md +236 -0
- jupyter_ext/agent/skills/data_cleaning_pipeline.md +12 -0
- jupyter_ext/agent/skills/data_loading.md +158 -0
- jupyter_ext/agent/skills/data_quality_report.md +13 -0
- jupyter_ext/agent/skills/dimensionality_reduction.md +12 -0
- jupyter_ext/agent/skills/emr_script_development.md +17 -0
- jupyter_ext/agent/skills/experiment_tracking.md +13 -0
- jupyter_ext/agent/skills/feature_importance_analysis.md +10 -0
- jupyter_ext/agent/skills/hyperparameter_tuning.md +12 -0
- jupyter_ext/agent/skills/hypothesis_testing.md +12 -0
- jupyter_ext/agent/skills/imbalanced_data_handling.md +12 -0
- jupyter_ext/agent/skills/inference.md +392 -0
- jupyter_ext/agent/skills/lambda_function_development.md +19 -0
- jupyter_ext/agent/skills/model_comparison.md +11 -0
- jupyter_ext/agent/skills/model_explainability.md +12 -0
- jupyter_ext/agent/skills/model_training.md +318 -0
- jupyter_ext/agent/skills/pyspark.md +352 -0
- jupyter_ext/agent/skills/python_code_quality.md +35 -0
- jupyter_ext/agent/skills/regression_evaluation.md +12 -0
- jupyter_ext/agent/skills/spring_java_development.md +35 -0
- jupyter_ext/agent/skills/text_preprocessing.md +12 -0
- jupyter_ext/agent/skills/time_series_analysis.md +13 -0
- jupyter_ext/agent/skills/unit_testing.md +203 -0
- jupyter_ext/agent/todo_prompts.py +57 -0
- jupyter_ext/agent/tool_report.py +45 -0
- jupyter_ext/analytics_ingest.py +599 -0
- jupyter_ext/etc/jupyter/jupyter_server_config.d/codebuff_agent.json +7 -0
- jupyter_ext/handlers/__init__.py +8 -0
- jupyter_ext/handlers/analytics_handler.py +170 -0
- jupyter_ext/handlers/benchmark_handler.py +794 -0
- jupyter_ext/handlers/eda_benchmark_handler.py +1328 -0
- jupyter_ext/handlers/rag_admin_handler.py +689 -0
- jupyter_ext/handlers/websocket.py +497 -0
- jupyter_ext/http_handlers.py +8744 -0
- jupyter_ext/idle_monitor.py +346 -0
- jupyter_ext/labextension/package.json +157 -0
- jupyter_ext/labextension/static/104.2b8f20f9d1ac2c9db01b.js +1 -0
- jupyter_ext/labextension/static/150.39fa98117cc8ca590465.js +1 -0
- jupyter_ext/labextension/static/166.386ff9b2c37ac865fdee.js +1 -0
- jupyter_ext/labextension/static/216.25cf81131bf7274dabf4.js +1 -0
- jupyter_ext/labextension/static/353.581bd1cb00746399b62e.js +1 -0
- jupyter_ext/labextension/static/367.575f9621b63c9a641aa9.js +1 -0
- jupyter_ext/labextension/static/421.11dac223a2c2da1b09f5.js +1 -0
- jupyter_ext/labextension/static/432.8513b4c6cc1d1232d165.js +2 -0
- jupyter_ext/labextension/static/432.8513b4c6cc1d1232d165.js.LICENSE.txt +19 -0
- jupyter_ext/labextension/static/439.ba5b5ef70ec6de214b8c.js +2 -0
- jupyter_ext/labextension/static/439.ba5b5ef70ec6de214b8c.js.LICENSE.txt +8 -0
- jupyter_ext/labextension/static/481.cc2dec9fa869d66f1407.js +1 -0
- jupyter_ext/labextension/static/487.0796e931d6e651482c14.js +1 -0
- jupyter_ext/labextension/static/49.d326f554b6f9ccde6580.js +1 -0
- jupyter_ext/labextension/static/541.26897ea32845659e36be.js +1 -0
- jupyter_ext/labextension/static/579.e569d3edd8391fd8fbb3.js +1 -0
- jupyter_ext/labextension/static/58.eb7cc679f7535cc8a2f1.js +2 -0
- jupyter_ext/labextension/static/58.eb7cc679f7535cc8a2f1.js.LICENSE.txt +8 -0
- jupyter_ext/labextension/static/666.d9bfdb23f98489d0a188.js +1 -0
- jupyter_ext/labextension/static/677.d266c39dbed33bc388b3.js +2 -0
- jupyter_ext/labextension/static/677.d266c39dbed33bc388b3.js.LICENSE.txt +8 -0
- jupyter_ext/labextension/static/68.26b6f59c8454adb17f20.js +204 -0
- jupyter_ext/labextension/static/694.4ac443814260057e68f7.js +1 -0
- jupyter_ext/labextension/static/791.61affb8d3480d3d60e0d.js +1 -0
- jupyter_ext/labextension/static/848.4d2db09cfbb09374b299.js +2 -0
- jupyter_ext/labextension/static/848.4d2db09cfbb09374b299.js.LICENSE.txt +9 -0
- jupyter_ext/labextension/static/868.5cfe7e1a348ec5af1b8c.js +1 -0
- jupyter_ext/labextension/static/97.ea559a5b8a9a09f17c6b.js +1 -0
- jupyter_ext/labextension/static/remoteEntry.5292fc9c6df7e7920cc8.js +1 -0
- jupyter_ext/labextension/static/style.js +4 -0
- jupyter_ext/labextension/static/third-party-licenses.json +724 -0
- jupyter_ext/log_paths.py +113 -0
- jupyter_ext/resource_usage.py +519 -0
- jupyter_ext/secrets_bootstrap.py +116 -0
- jupyter_ext/team_access.py +105 -0
- jupyter_ext/tests/__init__.py +1 -0
- jupyter_ext/tests/test_context.py +234 -0
- jupyter_ext/tests/test_factory.py +563 -0
- jupyter_ext/tests/test_file_finder.py +139 -0
- jupyter_ext/tests/test_handlers.py +58 -0
- jupyter_ext/tests/test_mcp_selftest.py +204 -0
- jupyter_ext/tests/test_mermaid_fixer.py +272 -0
- jupyter_ext/tests/test_resource_tiers.py +298 -0
- jupyter_ext/tools/__init__.py +15 -0
- jupyter_ext/tools/base.py +90 -0
- jupyter_ext/tools/context.py +268 -0
- jupyter_ext/tools/dir_complete.py +86 -0
- jupyter_ext/tools/file_finder.py +303 -0
- jupyter_ext/tools/file_tools.py +244 -0
- jupyter_ext/tools/jupyter_tools.py +296 -0
- jupyter_ext/tools/registry.py +159 -0
- jupyter_ext/tools/search_tools.py +230 -0
- jupyter_ext/tools/security_tools.py +657 -0
- jupyter_ext/tools/shell_tools.py +109 -0
- jupyter_ext/tools/user_tools.py +132 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Codebuff Core - ServiceFactory Tests
|
|
3
|
+
|
|
4
|
+
Tests for ServiceFactory initialization and service creation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from codebuff_core.factory import ServiceFactory, get_service_factory
|
|
10
|
+
from codebuff_core.interfaces import IAgentService, IChatService, IRAGService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestServiceFactorySingleton:
|
|
14
|
+
"""Tests for ServiceFactory singleton pattern"""
|
|
15
|
+
|
|
16
|
+
def test_get_instance_returns_same_instance(self, reset_factory):
|
|
17
|
+
"""Test get_instance returns the same instance"""
|
|
18
|
+
instance1 = ServiceFactory.get_instance()
|
|
19
|
+
instance2 = ServiceFactory.get_instance()
|
|
20
|
+
assert instance1 is instance2
|
|
21
|
+
|
|
22
|
+
def test_reset_instance_clears_singleton(self, reset_factory):
|
|
23
|
+
"""Test reset_instance clears the singleton"""
|
|
24
|
+
instance1 = ServiceFactory.get_instance()
|
|
25
|
+
ServiceFactory.reset_instance()
|
|
26
|
+
instance2 = ServiceFactory.get_instance()
|
|
27
|
+
assert instance1 is not instance2
|
|
28
|
+
|
|
29
|
+
def test_get_service_factory_convenience_function(self, reset_factory):
|
|
30
|
+
"""Test get_service_factory returns singleton"""
|
|
31
|
+
factory1 = get_service_factory()
|
|
32
|
+
factory2 = ServiceFactory.get_instance()
|
|
33
|
+
assert factory1 is factory2
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TestServiceFactoryInitialization:
|
|
37
|
+
"""Tests for ServiceFactory initialization"""
|
|
38
|
+
|
|
39
|
+
def test_not_initialized_by_default(self, reset_factory):
|
|
40
|
+
"""Test factory is not initialized by default"""
|
|
41
|
+
factory = ServiceFactory.get_instance()
|
|
42
|
+
assert factory.is_initialized is False
|
|
43
|
+
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_initialize(self, reset_factory, mock_env_embedded):
|
|
46
|
+
"""Test initialization creates embedded services"""
|
|
47
|
+
factory = ServiceFactory.get_instance()
|
|
48
|
+
await factory.initialize()
|
|
49
|
+
assert factory.is_initialized is True
|
|
50
|
+
|
|
51
|
+
@pytest.mark.asyncio
|
|
52
|
+
async def test_double_initialization_is_noop(
|
|
53
|
+
self, reset_factory, mock_env_embedded
|
|
54
|
+
):
|
|
55
|
+
"""Test calling initialize twice is safe"""
|
|
56
|
+
factory = ServiceFactory.get_instance()
|
|
57
|
+
await factory.initialize()
|
|
58
|
+
await factory.initialize() # Should not raise
|
|
59
|
+
assert factory.is_initialized is True
|
|
60
|
+
|
|
61
|
+
@pytest.mark.asyncio
|
|
62
|
+
async def test_shutdown_resets_state(self, reset_factory, mock_env_embedded):
|
|
63
|
+
"""Test shutdown resets initialization state"""
|
|
64
|
+
factory = ServiceFactory.get_instance()
|
|
65
|
+
await factory.initialize()
|
|
66
|
+
assert factory.is_initialized is True
|
|
67
|
+
await factory.shutdown()
|
|
68
|
+
assert factory.is_initialized is False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TestServiceFactoryServiceAccess:
|
|
72
|
+
"""Tests for service accessor methods"""
|
|
73
|
+
|
|
74
|
+
@pytest.mark.asyncio
|
|
75
|
+
async def test_get_agent_service_after_init(self, reset_factory, mock_env_embedded):
|
|
76
|
+
"""Test get_agent_service returns service after init"""
|
|
77
|
+
factory = ServiceFactory.get_instance()
|
|
78
|
+
await factory.initialize()
|
|
79
|
+
service = factory.get_agent_service()
|
|
80
|
+
assert service is not None
|
|
81
|
+
assert isinstance(service, IAgentService)
|
|
82
|
+
|
|
83
|
+
@pytest.mark.asyncio
|
|
84
|
+
async def test_get_chat_service_after_init(self, reset_factory, mock_env_embedded):
|
|
85
|
+
"""Test get_chat_service returns service after init"""
|
|
86
|
+
factory = ServiceFactory.get_instance()
|
|
87
|
+
await factory.initialize()
|
|
88
|
+
service = factory.get_chat_service()
|
|
89
|
+
assert service is not None
|
|
90
|
+
assert isinstance(service, IChatService)
|
|
91
|
+
|
|
92
|
+
@pytest.mark.asyncio
|
|
93
|
+
async def test_get_rag_service_after_init(self, reset_factory, mock_env_embedded):
|
|
94
|
+
"""Test get_rag_service returns service after init"""
|
|
95
|
+
factory = ServiceFactory.get_instance()
|
|
96
|
+
await factory.initialize()
|
|
97
|
+
service = factory.get_rag_service()
|
|
98
|
+
assert service is not None
|
|
99
|
+
assert isinstance(service, IRAGService)
|
|
100
|
+
|
|
101
|
+
def test_get_agent_service_before_init_raises(self, reset_factory):
|
|
102
|
+
"""Test get_agent_service raises before initialization"""
|
|
103
|
+
factory = ServiceFactory.get_instance()
|
|
104
|
+
with pytest.raises(RuntimeError, match="not initialized"):
|
|
105
|
+
factory.get_agent_service()
|
|
106
|
+
|
|
107
|
+
def test_get_chat_service_before_init_raises(self, reset_factory):
|
|
108
|
+
"""Test get_chat_service raises before initialization"""
|
|
109
|
+
factory = ServiceFactory.get_instance()
|
|
110
|
+
with pytest.raises(RuntimeError, match="not initialized"):
|
|
111
|
+
factory.get_chat_service()
|
|
112
|
+
|
|
113
|
+
def test_get_rag_service_before_init_raises(self, reset_factory):
|
|
114
|
+
"""Test get_rag_service raises before initialization"""
|
|
115
|
+
factory = ServiceFactory.get_instance()
|
|
116
|
+
with pytest.raises(RuntimeError, match="not initialized"):
|
|
117
|
+
factory.get_rag_service()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TestServiceFactoryServiceTypes:
|
|
121
|
+
"""Tests for service type verification"""
|
|
122
|
+
|
|
123
|
+
@pytest.mark.asyncio
|
|
124
|
+
async def test_creates_embedded_services(self, reset_factory, mock_env_embedded):
|
|
125
|
+
"""Test initialization creates EmbeddedXxxService instances"""
|
|
126
|
+
from codebuff_core.services.agent_service import EmbeddedAgentService
|
|
127
|
+
from codebuff_core.services.chat_service import EmbeddedChatService
|
|
128
|
+
from codebuff_core.services.rag_service import EmbeddedRAGService
|
|
129
|
+
|
|
130
|
+
factory = ServiceFactory.get_instance()
|
|
131
|
+
await factory.initialize()
|
|
132
|
+
|
|
133
|
+
assert isinstance(factory.get_agent_service(), EmbeddedAgentService)
|
|
134
|
+
assert isinstance(factory.get_chat_service(), EmbeddedChatService)
|
|
135
|
+
assert isinstance(factory.get_rag_service(), EmbeddedRAGService)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Codebuff Core - Service Implementation Tests
|
|
3
|
+
|
|
4
|
+
Tests for Embedded service implementations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from unittest.mock import AsyncMock, patch
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from codebuff_core.interfaces import IAgentService, IChatService, IRAGService
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestEmbeddedAgentService:
|
|
15
|
+
"""Tests for EmbeddedAgentService"""
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def embedded_agent_service(self):
|
|
19
|
+
"""Create EmbeddedAgentService instance"""
|
|
20
|
+
from codebuff_core.services.agent_service import EmbeddedAgentService
|
|
21
|
+
|
|
22
|
+
return EmbeddedAgentService()
|
|
23
|
+
|
|
24
|
+
def test_implements_interface(self, embedded_agent_service):
|
|
25
|
+
"""Test EmbeddedAgentService implements IAgentService"""
|
|
26
|
+
assert isinstance(embedded_agent_service, IAgentService)
|
|
27
|
+
|
|
28
|
+
@pytest.mark.asyncio
|
|
29
|
+
async def test_generate_plan_with_mock_llm(
|
|
30
|
+
self, embedded_agent_service, sample_plan_request
|
|
31
|
+
):
|
|
32
|
+
"""Test generate_plan calls LLM service"""
|
|
33
|
+
# Mock the LLM service
|
|
34
|
+
with patch.object(
|
|
35
|
+
embedded_agent_service, "_llm_service", create=True
|
|
36
|
+
) as mock_llm:
|
|
37
|
+
mock_llm.generate_response = AsyncMock(
|
|
38
|
+
return_value='{"steps": [], "plan_id": "test"}'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Note: Full integration test would require more mocking
|
|
42
|
+
# This is a placeholder for when LLM integration is complete
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TestEmbeddedChatService:
|
|
46
|
+
"""Tests for EmbeddedChatService"""
|
|
47
|
+
|
|
48
|
+
@pytest.fixture
|
|
49
|
+
def embedded_chat_service(self):
|
|
50
|
+
"""Create EmbeddedChatService instance"""
|
|
51
|
+
from codebuff_core.services.chat_service import EmbeddedChatService
|
|
52
|
+
|
|
53
|
+
return EmbeddedChatService()
|
|
54
|
+
|
|
55
|
+
def test_implements_interface(self, embedded_chat_service):
|
|
56
|
+
"""Test EmbeddedChatService implements IChatService"""
|
|
57
|
+
assert isinstance(embedded_chat_service, IChatService)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestEmbeddedRAGService:
|
|
61
|
+
"""Tests for EmbeddedRAGService"""
|
|
62
|
+
|
|
63
|
+
@pytest.fixture
|
|
64
|
+
def embedded_rag_service(self):
|
|
65
|
+
"""Create EmbeddedRAGService instance with mocked RAG manager"""
|
|
66
|
+
from codebuff_core.services.rag_service import EmbeddedRAGService
|
|
67
|
+
|
|
68
|
+
service = EmbeddedRAGService()
|
|
69
|
+
return service
|
|
70
|
+
|
|
71
|
+
def test_implements_interface(self, embedded_rag_service):
|
|
72
|
+
"""Test EmbeddedRAGService implements IRAGService"""
|
|
73
|
+
assert isinstance(embedded_rag_service, IRAGService)
|
|
74
|
+
|
|
75
|
+
def test_is_ready_before_init(self, embedded_rag_service):
|
|
76
|
+
"""Test is_ready returns False before initialization"""
|
|
77
|
+
assert embedded_rag_service.is_ready() is False
|
|
78
|
+
|
|
79
|
+
@pytest.mark.asyncio
|
|
80
|
+
async def test_initialize_sets_ready(self, embedded_rag_service, mock_rag_manager):
|
|
81
|
+
"""Test initialize makes service ready when RAGManager is available"""
|
|
82
|
+
# Directly set the internal state to simulate successful initialization
|
|
83
|
+
# since the actual rag_manager module might not be available
|
|
84
|
+
mock_rag_manager.is_ready = True
|
|
85
|
+
embedded_rag_service._rag_manager = mock_rag_manager
|
|
86
|
+
embedded_rag_service._initialized = True
|
|
87
|
+
|
|
88
|
+
assert embedded_rag_service.is_ready() is True
|
|
89
|
+
|
|
90
|
+
@pytest.mark.asyncio
|
|
91
|
+
async def test_initialize_is_noop(self, embedded_rag_service):
|
|
92
|
+
"""Test initialize is a no-op (agent_server dependency removed)"""
|
|
93
|
+
await embedded_rag_service.initialize()
|
|
94
|
+
|
|
95
|
+
# Service is not ready because RAG is no longer supported via agent_server
|
|
96
|
+
assert embedded_rag_service.is_ready() is False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TestServiceInterfaceCompliance:
|
|
100
|
+
"""Tests to verify all services implement their interfaces correctly"""
|
|
101
|
+
|
|
102
|
+
def test_all_agent_service_methods_exist(self):
|
|
103
|
+
"""Verify all IAgentService methods are implemented"""
|
|
104
|
+
from codebuff_core.services.agent_service import EmbeddedAgentService
|
|
105
|
+
|
|
106
|
+
required_methods = [
|
|
107
|
+
"generate_plan",
|
|
108
|
+
"refine_code",
|
|
109
|
+
"replan",
|
|
110
|
+
"validate_code",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
for method in required_methods:
|
|
114
|
+
assert hasattr(EmbeddedAgentService, method), (
|
|
115
|
+
f"EmbeddedAgentService missing {method}"
|
|
116
|
+
)
|
|
117
|
+
assert callable(getattr(EmbeddedAgentService, method))
|
|
118
|
+
|
|
119
|
+
def test_all_chat_service_methods_exist(self):
|
|
120
|
+
"""Verify all IChatService methods are implemented"""
|
|
121
|
+
from codebuff_core.services.chat_service import EmbeddedChatService
|
|
122
|
+
|
|
123
|
+
required_methods = [
|
|
124
|
+
"send_message",
|
|
125
|
+
"send_message_stream",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
for method in required_methods:
|
|
129
|
+
assert hasattr(EmbeddedChatService, method), (
|
|
130
|
+
f"EmbeddedChatService missing {method}"
|
|
131
|
+
)
|
|
132
|
+
assert callable(getattr(EmbeddedChatService, method))
|
|
133
|
+
|
|
134
|
+
def test_all_rag_service_methods_exist(self):
|
|
135
|
+
"""Verify all IRAGService methods are implemented"""
|
|
136
|
+
from codebuff_core.services.rag_service import EmbeddedRAGService
|
|
137
|
+
|
|
138
|
+
required_methods = [
|
|
139
|
+
"search",
|
|
140
|
+
"get_context_for_query",
|
|
141
|
+
"is_ready",
|
|
142
|
+
"get_index_status",
|
|
143
|
+
"trigger_reindex",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
for method in required_methods:
|
|
147
|
+
assert hasattr(EmbeddedRAGService, method), (
|
|
148
|
+
f"EmbeddedRAGService missing {method}"
|
|
149
|
+
)
|
|
150
|
+
assert callable(getattr(EmbeddedRAGService, method))
|
jupyter_ext/__init__.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Codebuff Agent.
|
|
3
|
+
|
|
4
|
+
In-process execution via ServiceFactory and WebSocket agent.
|
|
5
|
+
Configuration: ~/.jupyter/codebuff_config.json
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import traceback
|
|
12
|
+
|
|
13
|
+
from ._version import __version__
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _setup_local_imports():
|
|
17
|
+
"""Add local codebuff_core to sys.path for direct source imports."""
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
# jupyter_ext/__init__.py → extensions/jupyter/jupyter_ext/
|
|
21
|
+
# codebuff_core source: repo_root/codebuff_core/
|
|
22
|
+
repo_root = Path(__file__).resolve().parent.parent.parent.parent
|
|
23
|
+
core_path = str(repo_root / "codebuff_core")
|
|
24
|
+
if core_path not in sys.path:
|
|
25
|
+
sys.path.insert(0, core_path)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_setup_local_imports()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# SSL certificate setup for macOS
|
|
32
|
+
if sys.platform == "darwin":
|
|
33
|
+
try:
|
|
34
|
+
import certifi
|
|
35
|
+
|
|
36
|
+
os.environ["SSL_CERT_FILE"] = certifi.where()
|
|
37
|
+
os.environ["REQUESTS_CA_BUNDLE"] = certifi.where()
|
|
38
|
+
except ImportError:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _jupyter_labextension_paths():
|
|
43
|
+
"""Called by JupyterLab to find extension."""
|
|
44
|
+
return [{"src": "labextension", "dest": "codebuff"}]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _jupyter_server_extension_points():
|
|
48
|
+
"""Called by Jupyter Server to enable extension."""
|
|
49
|
+
return [{"module": "jupyter_ext"}]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _ensure_config_files():
|
|
53
|
+
"""Automatically create necessary configuration files on first run."""
|
|
54
|
+
import json
|
|
55
|
+
import shutil
|
|
56
|
+
from pathlib import Path
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
jupyter_config_dir = Path.home() / ".jupyter"
|
|
60
|
+
jupyter_server_config_d = jupyter_config_dir / "jupyter_server_config.d"
|
|
61
|
+
|
|
62
|
+
# 1. Create jupyter_server_config.d directory
|
|
63
|
+
jupyter_server_config_d.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
|
|
65
|
+
# 2. Copy codebuff.json (server extension registration)
|
|
66
|
+
dest_server_config = jupyter_server_config_d / "codebuff_agent.json"
|
|
67
|
+
if not dest_server_config.exists():
|
|
68
|
+
source_server_config = (
|
|
69
|
+
Path(__file__).parent
|
|
70
|
+
/ "etc"
|
|
71
|
+
/ "jupyter"
|
|
72
|
+
/ "jupyter_server_config.d"
|
|
73
|
+
/ "codebuff_agent.json"
|
|
74
|
+
)
|
|
75
|
+
if source_server_config.exists():
|
|
76
|
+
shutil.copy(source_server_config, dest_server_config)
|
|
77
|
+
|
|
78
|
+
# 3. Create codebuff_config.json (Agent settings - default values)
|
|
79
|
+
config_file = jupyter_config_dir / "codebuff_config.json"
|
|
80
|
+
legacy_config_file = jupyter_config_dir / "hdsp_agent_config.json"
|
|
81
|
+
if not config_file.exists() and not legacy_config_file.exists():
|
|
82
|
+
default_config = {
|
|
83
|
+
"provider": "vllm",
|
|
84
|
+
"vllm": {
|
|
85
|
+
"endpoint": "https://openrouter.ai/api/v1",
|
|
86
|
+
"apiKey": "",
|
|
87
|
+
"model": "openai/gpt-oss-120b",
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
with open(config_file, "w") as f:
|
|
91
|
+
json.dump(default_config, f, indent=2)
|
|
92
|
+
|
|
93
|
+
except Exception:
|
|
94
|
+
# Configuration file creation failure is not fatal, ignore silently
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def _initialize_service_factory(server_app):
|
|
99
|
+
"""Initialize ServiceFactory if codebuff_core is available."""
|
|
100
|
+
try:
|
|
101
|
+
from codebuff_core.factory import get_service_factory
|
|
102
|
+
|
|
103
|
+
factory = get_service_factory()
|
|
104
|
+
await factory.initialize()
|
|
105
|
+
|
|
106
|
+
server_app.log.info("Codebuff ServiceFactory initialized")
|
|
107
|
+
|
|
108
|
+
rag_service = factory.get_rag_service()
|
|
109
|
+
rag_ready = rag_service.is_ready()
|
|
110
|
+
server_app.log.info(f" RAG service ready: {rag_ready}")
|
|
111
|
+
|
|
112
|
+
except ImportError:
|
|
113
|
+
# codebuff_core not available, skip ServiceFactory initialization
|
|
114
|
+
pass
|
|
115
|
+
except Exception as e:
|
|
116
|
+
server_app.log.warning(f"ServiceFactory initialization failed: {e}")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _schedule_initialization(server_app):
|
|
120
|
+
"""Schedule async initialization in the event loop."""
|
|
121
|
+
try:
|
|
122
|
+
loop = asyncio.get_event_loop()
|
|
123
|
+
if loop.is_running():
|
|
124
|
+
asyncio.ensure_future(_initialize_service_factory(server_app))
|
|
125
|
+
else:
|
|
126
|
+
loop.run_until_complete(_initialize_service_factory(server_app))
|
|
127
|
+
except RuntimeError:
|
|
128
|
+
asyncio.run(_initialize_service_factory(server_app))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def load_jupyter_server_extension(server_app):
|
|
132
|
+
"""Load the Jupyter Server extension."""
|
|
133
|
+
# [Auto-config] Create config files if they don't exist
|
|
134
|
+
_ensure_config_files()
|
|
135
|
+
|
|
136
|
+
# [Secrets] AWS Secrets Manager → 연결정보 env 주입 (HDSP_ENV=dev/prod일 때).
|
|
137
|
+
# 서비스/ConfigManager가 읽기 전에 실행. 실패해도 기동은 계속(채팅 시 노출).
|
|
138
|
+
try:
|
|
139
|
+
from .secrets_bootstrap import bootstrap_secrets
|
|
140
|
+
|
|
141
|
+
bootstrap_secrets()
|
|
142
|
+
except Exception as _e: # noqa: BLE001
|
|
143
|
+
server_app.log.warning(f"[Secrets] bootstrap skipped: {_e}")
|
|
144
|
+
|
|
145
|
+
# [Central config] centri 에서 조직 설정을 주기적으로 받는다 (이슈 #113).
|
|
146
|
+
# env 주입 뒤여야 URL 을 본다. URL 이 없으면 스레드를 띄우지 않는다.
|
|
147
|
+
try:
|
|
148
|
+
from codebuff_core.managers.remote_config import start_from_env
|
|
149
|
+
|
|
150
|
+
from .team_access import resolve_team, user_ids
|
|
151
|
+
|
|
152
|
+
# 팀은 env 가 비면 JWT 팀 클레임으로 — centri 가 팀 단위 값을 고르는 축이다.
|
|
153
|
+
# 사번은 로그 대조용이다 — 사용자 층은 centri 가 토큰으로 고른다 (이슈 #181)
|
|
154
|
+
start_from_env(team=resolve_team(), user_ids=user_ids())
|
|
155
|
+
except Exception as _e: # noqa: BLE001
|
|
156
|
+
server_app.log.warning(f"[Central config] start skipped: {_e}")
|
|
157
|
+
|
|
158
|
+
# [Custom skills] centri 포털에서 편집한 스킬을 받아 동기화 디렉터리에 쓴다 (이슈 #134).
|
|
159
|
+
# 기동은 첫 받기를 기다리지 않는다. URL 이 없으면 스레드를 띄우지 않는다.
|
|
160
|
+
# 같은 스레드가 로컬 스킬 목록을 centri 에 보고한다 (이슈 #182).
|
|
161
|
+
try:
|
|
162
|
+
import functools
|
|
163
|
+
|
|
164
|
+
from codebuff_core.managers import remote_skills
|
|
165
|
+
|
|
166
|
+
from .agent.tool_report import agent_tools_report
|
|
167
|
+
from .http_handlers import local_skills_report
|
|
168
|
+
from .team_access import resolve_team, user_ids
|
|
169
|
+
|
|
170
|
+
remote_skills.start_from_env(
|
|
171
|
+
team=resolve_team(),
|
|
172
|
+
project=os.environ.get("HDSP_PRJ_ID", ""),
|
|
173
|
+
user_ids=user_ids(),
|
|
174
|
+
local_source=functools.partial(
|
|
175
|
+
local_skills_report,
|
|
176
|
+
server_app.web_app.settings.get("server_root_dir", os.getcwd()),
|
|
177
|
+
),
|
|
178
|
+
# 에이전트 도구 정의도 같은 보고에 싣는다 (이슈 #248)
|
|
179
|
+
tools_source=agent_tools_report,
|
|
180
|
+
)
|
|
181
|
+
except Exception as _e: # noqa: BLE001
|
|
182
|
+
server_app.log.warning(f"[Custom skills] start skipped: {_e}")
|
|
183
|
+
|
|
184
|
+
# [Background] 지난 서버가 kill -9 로 죽으며 남긴 백그라운드 프로세스를 거둔다 (이슈 #130)
|
|
185
|
+
try:
|
|
186
|
+
from .agent.background_procs import reap_orphans
|
|
187
|
+
|
|
188
|
+
reaped = reap_orphans()
|
|
189
|
+
if reaped:
|
|
190
|
+
server_app.log.info(f"[Background] 남은 프로세스 {reaped}개를 거뒀습니다")
|
|
191
|
+
except Exception as _e: # noqa: BLE001
|
|
192
|
+
server_app.log.warning(f"[Background] reap skipped: {_e}")
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
from .http_handlers import setup_handlers
|
|
196
|
+
|
|
197
|
+
web_app = server_app.web_app
|
|
198
|
+
setup_handlers(web_app)
|
|
199
|
+
|
|
200
|
+
# Expose environment variables to client via PageConfig
|
|
201
|
+
# This allows frontend to access HDSP_ENV, HDSP_PRJ_ID, JUPYTERHUB_USER
|
|
202
|
+
page_config = web_app.settings.setdefault("page_config_data", {})
|
|
203
|
+
|
|
204
|
+
# Add HDSP environment variables
|
|
205
|
+
hdsp_env = os.environ.get("HDSP_ENV", "")
|
|
206
|
+
hdsp_prj_id = os.environ.get("HDSP_PRJ_ID", "")
|
|
207
|
+
jupyterhub_user = os.environ.get("JUPYTERHUB_USER", "")
|
|
208
|
+
hdsp_bdp_id = os.environ.get("HDSP_BDP_ID", "")
|
|
209
|
+
|
|
210
|
+
if hdsp_env:
|
|
211
|
+
page_config["HDSP_ENV"] = hdsp_env
|
|
212
|
+
if hdsp_prj_id:
|
|
213
|
+
page_config["HDSP_PRJ_ID"] = hdsp_prj_id
|
|
214
|
+
if jupyterhub_user:
|
|
215
|
+
page_config["JUPYTERHUB_USER"] = jupyterhub_user
|
|
216
|
+
if hdsp_bdp_id:
|
|
217
|
+
page_config["HDSP_BDP_ID"] = hdsp_bdp_id
|
|
218
|
+
|
|
219
|
+
server_app.log.info(
|
|
220
|
+
f"Exposed HDSP env vars to client: HDSP_ENV={hdsp_env}, HDSP_PRJ_ID={hdsp_prj_id}, JUPYTERHUB_USER={jupyterhub_user}, HDSP_BDP_ID={hdsp_bdp_id}"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Keycloak JWT: Extract team claim for access control
|
|
224
|
+
from .team_access import read_user_team
|
|
225
|
+
|
|
226
|
+
team_claim_name = os.environ.get("HDSP_TEAM_CLAIM_NAME", "hcc_team")
|
|
227
|
+
user_team = read_user_team()
|
|
228
|
+
if user_team:
|
|
229
|
+
server_app.log.info(
|
|
230
|
+
f"Decoded Keycloak JWT: team claim '{team_claim_name}' = '{user_team}'"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
page_config["HDSP_USER_TEAM"] = user_team
|
|
234
|
+
page_config["HDSP_TEAM_CLAIM_NAME"] = team_claim_name
|
|
235
|
+
page_config["CODEBUFF_EXTENSION_VERSION"] = __version__
|
|
236
|
+
|
|
237
|
+
# Build time
|
|
238
|
+
from pathlib import Path
|
|
239
|
+
|
|
240
|
+
_build_time_path = Path(__file__).parent / "_build_time.txt"
|
|
241
|
+
_build_time = ""
|
|
242
|
+
if _build_time_path.exists():
|
|
243
|
+
_build_time = _build_time_path.read_text().strip()
|
|
244
|
+
page_config["CODEBUFF_BUILD_TIME"] = _build_time
|
|
245
|
+
|
|
246
|
+
server_app.log.info(f"User team from Keycloak: {user_team or '(not set)'}")
|
|
247
|
+
|
|
248
|
+
server_app.log.info("Codebuff Agent loaded (v%s)", __version__)
|
|
249
|
+
_schedule_initialization(server_app)
|
|
250
|
+
|
|
251
|
+
# Start server-side idle monitor
|
|
252
|
+
try:
|
|
253
|
+
from .idle_monitor import ServerIdleMonitor
|
|
254
|
+
|
|
255
|
+
idle_monitor = ServerIdleMonitor(server_app)
|
|
256
|
+
idle_monitor.start()
|
|
257
|
+
web_app.settings["codebuff_idle_monitor"] = idle_monitor
|
|
258
|
+
except Exception as idle_err:
|
|
259
|
+
server_app.log.warning(
|
|
260
|
+
f"Server-side idle monitor failed to start: {idle_err}"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
server_app.log.error(f"Failed to load Codebuff Agent: {e}")
|
|
265
|
+
server_app.log.error(traceback.format_exc())
|
|
266
|
+
raise e
|
jupyter_ext/_version.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Agent module for Codebuff.
|
|
2
|
+
|
|
3
|
+
All imports are lazy to avoid pulling in deepagents/langgraph at module load time.
|
|
4
|
+
This prevents ImportError when deepagents is missing or has breaking changes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib as _importlib
|
|
8
|
+
|
|
9
|
+
# Backward compatibility alias
|
|
10
|
+
DeepAgentRunner = None # populated on first access
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AgentConfig",
|
|
14
|
+
"AgentEvent",
|
|
15
|
+
"AgentFactory",
|
|
16
|
+
"AgentRunner",
|
|
17
|
+
"DSMemoryMiddleware",
|
|
18
|
+
"DeepAgentRunner",
|
|
19
|
+
"MemoryEntry",
|
|
20
|
+
"MemoryStore",
|
|
21
|
+
"config_from_dict",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
_FACTORY_NAMES = {
|
|
25
|
+
"AgentConfig",
|
|
26
|
+
"AgentEvent",
|
|
27
|
+
"AgentFactory",
|
|
28
|
+
"AgentRunner",
|
|
29
|
+
"config_from_dict",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_MIDDLEWARE_NAMES = {
|
|
33
|
+
"DSMemoryMiddleware",
|
|
34
|
+
"MemoryEntry",
|
|
35
|
+
"MemoryStore",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def __getattr__(name: str):
|
|
40
|
+
if name in _FACTORY_NAMES:
|
|
41
|
+
mod = _importlib.import_module(".agent_factory", __name__)
|
|
42
|
+
return getattr(mod, name)
|
|
43
|
+
if name in _MIDDLEWARE_NAMES:
|
|
44
|
+
mod = _importlib.import_module(".middleware", __name__)
|
|
45
|
+
return getattr(mod, name)
|
|
46
|
+
if name == "DeepAgentRunner":
|
|
47
|
+
mod = _importlib.import_module(".agent_factory", __name__)
|
|
48
|
+
return mod.AgentRunner
|
|
49
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|