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.
Files changed (217) hide show
  1. codebuff_agent-2.1.74.data/data/etc/jupyter/jupyter_server_config.d/codebuff_agent.json +7 -0
  2. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/package.json +157 -0
  3. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/104.2b8f20f9d1ac2c9db01b.js +1 -0
  4. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/150.39fa98117cc8ca590465.js +1 -0
  5. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/166.386ff9b2c37ac865fdee.js +1 -0
  6. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/216.25cf81131bf7274dabf4.js +1 -0
  7. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/353.581bd1cb00746399b62e.js +1 -0
  8. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/367.575f9621b63c9a641aa9.js +1 -0
  9. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/421.11dac223a2c2da1b09f5.js +1 -0
  10. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/432.8513b4c6cc1d1232d165.js +2 -0
  11. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/432.8513b4c6cc1d1232d165.js.LICENSE.txt +19 -0
  12. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/439.ba5b5ef70ec6de214b8c.js +2 -0
  13. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/439.ba5b5ef70ec6de214b8c.js.LICENSE.txt +8 -0
  14. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/481.cc2dec9fa869d66f1407.js +1 -0
  15. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/487.0796e931d6e651482c14.js +1 -0
  16. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/49.d326f554b6f9ccde6580.js +1 -0
  17. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/541.26897ea32845659e36be.js +1 -0
  18. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/579.e569d3edd8391fd8fbb3.js +1 -0
  19. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/58.eb7cc679f7535cc8a2f1.js +2 -0
  20. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/58.eb7cc679f7535cc8a2f1.js.LICENSE.txt +8 -0
  21. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/666.d9bfdb23f98489d0a188.js +1 -0
  22. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/677.d266c39dbed33bc388b3.js +2 -0
  23. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/677.d266c39dbed33bc388b3.js.LICENSE.txt +8 -0
  24. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/68.26b6f59c8454adb17f20.js +204 -0
  25. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/694.4ac443814260057e68f7.js +1 -0
  26. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/791.61affb8d3480d3d60e0d.js +1 -0
  27. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/848.4d2db09cfbb09374b299.js +2 -0
  28. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/848.4d2db09cfbb09374b299.js.LICENSE.txt +9 -0
  29. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/868.5cfe7e1a348ec5af1b8c.js +1 -0
  30. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/97.ea559a5b8a9a09f17c6b.js +1 -0
  31. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/remoteEntry.5292fc9c6df7e7920cc8.js +1 -0
  32. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/style.js +4 -0
  33. codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/third-party-licenses.json +724 -0
  34. codebuff_agent-2.1.74.dist-info/METADATA +142 -0
  35. codebuff_agent-2.1.74.dist-info/RECORD +217 -0
  36. codebuff_agent-2.1.74.dist-info/WHEEL +4 -0
  37. codebuff_agent-2.1.74.dist-info/licenses/LICENSE +21 -0
  38. codebuff_core/__init__.py +153 -0
  39. codebuff_core/codebuff_config_defaults.json +64 -0
  40. codebuff_core/core/__init__.py +35 -0
  41. codebuff_core/core/code_validator.py +1238 -0
  42. codebuff_core/core/error_classifier.py +579 -0
  43. codebuff_core/core/exceptions.py +49 -0
  44. codebuff_core/data/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +100256 -0
  45. codebuff_core/directory_policy.py +32 -0
  46. codebuff_core/factory.py +155 -0
  47. codebuff_core/interfaces.py +197 -0
  48. codebuff_core/knowledge/__init__.py +31 -0
  49. codebuff_core/knowledge/chunking.py +375 -0
  50. codebuff_core/knowledge/loader.py +339 -0
  51. codebuff_core/knowledge/watchdog_service.py +354 -0
  52. codebuff_core/legacy_paths.py +69 -0
  53. codebuff_core/llm/__init__.py +13 -0
  54. codebuff_core/llm/service.py +407 -0
  55. codebuff_core/managers/__init__.py +22 -0
  56. codebuff_core/managers/config_manager.py +690 -0
  57. codebuff_core/managers/remote_config.py +673 -0
  58. codebuff_core/managers/remote_skills.py +539 -0
  59. codebuff_core/managers/session_manager.py +255 -0
  60. codebuff_core/middleware/__init__.py +3 -0
  61. codebuff_core/middleware/error_recovery.py +394 -0
  62. codebuff_core/middleware/logging_utils.py +33 -0
  63. codebuff_core/models/__init__.py +111 -0
  64. codebuff_core/models/agent.py +306 -0
  65. codebuff_core/models/chat.py +57 -0
  66. codebuff_core/models/common.py +82 -0
  67. codebuff_core/models/rag.py +300 -0
  68. codebuff_core/platform_rules.py +127 -0
  69. codebuff_core/prompts/__init__.py +44 -0
  70. codebuff_core/prompts/auto_agent_prompts.py +1278 -0
  71. codebuff_core/services/__init__.py +30 -0
  72. codebuff_core/services/agent_service.py +345 -0
  73. codebuff_core/services/analytics_aggregate.py +837 -0
  74. codebuff_core/services/chat_service.py +898 -0
  75. codebuff_core/services/context_condenser.py +306 -0
  76. codebuff_core/services/pii_guardrail.py +393 -0
  77. codebuff_core/services/rag_chat_agent.py +2993 -0
  78. codebuff_core/services/rag_collections.py +2740 -0
  79. codebuff_core/services/rag_manager.py +890 -0
  80. codebuff_core/services/rag_service.py +145 -0
  81. codebuff_core/services/retriever.py +260 -0
  82. codebuff_core/services/vllm_embedding_service.py +290 -0
  83. codebuff_core/tests/__init__.py +1 -0
  84. codebuff_core/tests/conftest.py +81 -0
  85. codebuff_core/tests/test_error_recovery.py +647 -0
  86. codebuff_core/tests/test_factory.py +135 -0
  87. codebuff_core/tests/test_services.py +150 -0
  88. jupyter_ext/__init__.py +266 -0
  89. jupyter_ext/_version.py +4 -0
  90. jupyter_ext/agent/__init__.py +49 -0
  91. jupyter_ext/agent/agent_factory.py +2270 -0
  92. jupyter_ext/agent/ast_tools.py +451 -0
  93. jupyter_ext/agent/autofix_middleware.py +481 -0
  94. jupyter_ext/agent/background_procs.py +372 -0
  95. jupyter_ext/agent/code_history_middleware.py +211 -0
  96. jupyter_ext/agent/codex_autofix.py +160 -0
  97. jupyter_ext/agent/codex_bridge.py +3339 -0
  98. jupyter_ext/agent/codex_hooks.py +320 -0
  99. jupyter_ext/agent/codex_slash.py +685 -0
  100. jupyter_ext/agent/codex_threads.py +114 -0
  101. jupyter_ext/agent/content_injection.py +377 -0
  102. jupyter_ext/agent/continuation_middleware.py +548 -0
  103. jupyter_ext/agent/custom_skills.py +335 -0
  104. jupyter_ext/agent/file_diffs.py +99 -0
  105. jupyter_ext/agent/git_history.py +188 -0
  106. jupyter_ext/agent/guardrail_middleware.py +421 -0
  107. jupyter_ext/agent/json_repair_utils.py +54 -0
  108. jupyter_ext/agent/langchain_tools.py +2673 -0
  109. jupyter_ext/agent/mermaid_fixer.py +289 -0
  110. jupyter_ext/agent/mermaid_middleware.py +135 -0
  111. jupyter_ext/agent/middleware.py +359 -0
  112. jupyter_ext/agent/middleware_prompts.py +53 -0
  113. jupyter_ext/agent/observability.py +334 -0
  114. jupyter_ext/agent/patch_tool_calls.py +57 -0
  115. jupyter_ext/agent/prompts.py +112 -0
  116. jupyter_ext/agent/skill_middleware.py +257 -0
  117. jupyter_ext/agent/skills/airflow_dag_development.md +36 -0
  118. jupyter_ext/agent/skills/anomaly_detection.md +12 -0
  119. jupyter_ext/agent/skills/athena_sql.md +156 -0
  120. jupyter_ext/agent/skills/bdp_table_search.md +33 -0
  121. jupyter_ext/agent/skills/bigquery.md +65 -0
  122. jupyter_ext/agent/skills/bigquery_oracle.md +231 -0
  123. jupyter_ext/agent/skills/bigquery_tuning.md +307 -0
  124. jupyter_ext/agent/skills/classification_evaluation.md +11 -0
  125. jupyter_ext/agent/skills/clustering_analysis.md +12 -0
  126. jupyter_ext/agent/skills/comprehensive_eda.md +11 -0
  127. jupyter_ext/agent/skills/data_analysis.md +236 -0
  128. jupyter_ext/agent/skills/data_cleaning_pipeline.md +12 -0
  129. jupyter_ext/agent/skills/data_loading.md +158 -0
  130. jupyter_ext/agent/skills/data_quality_report.md +13 -0
  131. jupyter_ext/agent/skills/dimensionality_reduction.md +12 -0
  132. jupyter_ext/agent/skills/emr_script_development.md +17 -0
  133. jupyter_ext/agent/skills/experiment_tracking.md +13 -0
  134. jupyter_ext/agent/skills/feature_importance_analysis.md +10 -0
  135. jupyter_ext/agent/skills/hyperparameter_tuning.md +12 -0
  136. jupyter_ext/agent/skills/hypothesis_testing.md +12 -0
  137. jupyter_ext/agent/skills/imbalanced_data_handling.md +12 -0
  138. jupyter_ext/agent/skills/inference.md +392 -0
  139. jupyter_ext/agent/skills/lambda_function_development.md +19 -0
  140. jupyter_ext/agent/skills/model_comparison.md +11 -0
  141. jupyter_ext/agent/skills/model_explainability.md +12 -0
  142. jupyter_ext/agent/skills/model_training.md +318 -0
  143. jupyter_ext/agent/skills/pyspark.md +352 -0
  144. jupyter_ext/agent/skills/python_code_quality.md +35 -0
  145. jupyter_ext/agent/skills/regression_evaluation.md +12 -0
  146. jupyter_ext/agent/skills/spring_java_development.md +35 -0
  147. jupyter_ext/agent/skills/text_preprocessing.md +12 -0
  148. jupyter_ext/agent/skills/time_series_analysis.md +13 -0
  149. jupyter_ext/agent/skills/unit_testing.md +203 -0
  150. jupyter_ext/agent/todo_prompts.py +57 -0
  151. jupyter_ext/agent/tool_report.py +45 -0
  152. jupyter_ext/analytics_ingest.py +599 -0
  153. jupyter_ext/etc/jupyter/jupyter_server_config.d/codebuff_agent.json +7 -0
  154. jupyter_ext/handlers/__init__.py +8 -0
  155. jupyter_ext/handlers/analytics_handler.py +170 -0
  156. jupyter_ext/handlers/benchmark_handler.py +794 -0
  157. jupyter_ext/handlers/eda_benchmark_handler.py +1328 -0
  158. jupyter_ext/handlers/rag_admin_handler.py +689 -0
  159. jupyter_ext/handlers/websocket.py +497 -0
  160. jupyter_ext/http_handlers.py +8744 -0
  161. jupyter_ext/idle_monitor.py +346 -0
  162. jupyter_ext/labextension/package.json +157 -0
  163. jupyter_ext/labextension/static/104.2b8f20f9d1ac2c9db01b.js +1 -0
  164. jupyter_ext/labextension/static/150.39fa98117cc8ca590465.js +1 -0
  165. jupyter_ext/labextension/static/166.386ff9b2c37ac865fdee.js +1 -0
  166. jupyter_ext/labextension/static/216.25cf81131bf7274dabf4.js +1 -0
  167. jupyter_ext/labextension/static/353.581bd1cb00746399b62e.js +1 -0
  168. jupyter_ext/labextension/static/367.575f9621b63c9a641aa9.js +1 -0
  169. jupyter_ext/labextension/static/421.11dac223a2c2da1b09f5.js +1 -0
  170. jupyter_ext/labextension/static/432.8513b4c6cc1d1232d165.js +2 -0
  171. jupyter_ext/labextension/static/432.8513b4c6cc1d1232d165.js.LICENSE.txt +19 -0
  172. jupyter_ext/labextension/static/439.ba5b5ef70ec6de214b8c.js +2 -0
  173. jupyter_ext/labextension/static/439.ba5b5ef70ec6de214b8c.js.LICENSE.txt +8 -0
  174. jupyter_ext/labextension/static/481.cc2dec9fa869d66f1407.js +1 -0
  175. jupyter_ext/labextension/static/487.0796e931d6e651482c14.js +1 -0
  176. jupyter_ext/labextension/static/49.d326f554b6f9ccde6580.js +1 -0
  177. jupyter_ext/labextension/static/541.26897ea32845659e36be.js +1 -0
  178. jupyter_ext/labextension/static/579.e569d3edd8391fd8fbb3.js +1 -0
  179. jupyter_ext/labextension/static/58.eb7cc679f7535cc8a2f1.js +2 -0
  180. jupyter_ext/labextension/static/58.eb7cc679f7535cc8a2f1.js.LICENSE.txt +8 -0
  181. jupyter_ext/labextension/static/666.d9bfdb23f98489d0a188.js +1 -0
  182. jupyter_ext/labextension/static/677.d266c39dbed33bc388b3.js +2 -0
  183. jupyter_ext/labextension/static/677.d266c39dbed33bc388b3.js.LICENSE.txt +8 -0
  184. jupyter_ext/labextension/static/68.26b6f59c8454adb17f20.js +204 -0
  185. jupyter_ext/labextension/static/694.4ac443814260057e68f7.js +1 -0
  186. jupyter_ext/labextension/static/791.61affb8d3480d3d60e0d.js +1 -0
  187. jupyter_ext/labextension/static/848.4d2db09cfbb09374b299.js +2 -0
  188. jupyter_ext/labextension/static/848.4d2db09cfbb09374b299.js.LICENSE.txt +9 -0
  189. jupyter_ext/labextension/static/868.5cfe7e1a348ec5af1b8c.js +1 -0
  190. jupyter_ext/labextension/static/97.ea559a5b8a9a09f17c6b.js +1 -0
  191. jupyter_ext/labextension/static/remoteEntry.5292fc9c6df7e7920cc8.js +1 -0
  192. jupyter_ext/labextension/static/style.js +4 -0
  193. jupyter_ext/labextension/static/third-party-licenses.json +724 -0
  194. jupyter_ext/log_paths.py +113 -0
  195. jupyter_ext/resource_usage.py +519 -0
  196. jupyter_ext/secrets_bootstrap.py +116 -0
  197. jupyter_ext/team_access.py +105 -0
  198. jupyter_ext/tests/__init__.py +1 -0
  199. jupyter_ext/tests/test_context.py +234 -0
  200. jupyter_ext/tests/test_factory.py +563 -0
  201. jupyter_ext/tests/test_file_finder.py +139 -0
  202. jupyter_ext/tests/test_handlers.py +58 -0
  203. jupyter_ext/tests/test_mcp_selftest.py +204 -0
  204. jupyter_ext/tests/test_mermaid_fixer.py +272 -0
  205. jupyter_ext/tests/test_resource_tiers.py +298 -0
  206. jupyter_ext/tools/__init__.py +15 -0
  207. jupyter_ext/tools/base.py +90 -0
  208. jupyter_ext/tools/context.py +268 -0
  209. jupyter_ext/tools/dir_complete.py +86 -0
  210. jupyter_ext/tools/file_finder.py +303 -0
  211. jupyter_ext/tools/file_tools.py +244 -0
  212. jupyter_ext/tools/jupyter_tools.py +296 -0
  213. jupyter_ext/tools/registry.py +159 -0
  214. jupyter_ext/tools/search_tools.py +230 -0
  215. jupyter_ext/tools/security_tools.py +657 -0
  216. jupyter_ext/tools/shell_tools.py +109 -0
  217. 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))
@@ -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
@@ -0,0 +1,4 @@
1
+ # This file is auto-generated by Hatchling. As such, do not:
2
+ # - modify
3
+ # - track in version control e.g. be sure to add to .gitignore
4
+ __version__ = VERSION = '2.1.74'
@@ -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}")