devtorch-core 3.0.1__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 (193) hide show
  1. devtorch_core/__init__.py +158 -0
  2. devtorch_core/aggphi_textual.py +275 -0
  3. devtorch_core/alerts/__init__.py +23 -0
  4. devtorch_core/alerts/base.py +46 -0
  5. devtorch_core/alerts/config.py +60 -0
  6. devtorch_core/alerts/dispatcher.py +110 -0
  7. devtorch_core/alerts/jira.py +96 -0
  8. devtorch_core/alerts/linear.py +72 -0
  9. devtorch_core/alerts/pagerduty.py +66 -0
  10. devtorch_core/alerts/slack.py +81 -0
  11. devtorch_core/alerts/teams.py +70 -0
  12. devtorch_core/audit/__init__.py +43 -0
  13. devtorch_core/audit/exporter.py +297 -0
  14. devtorch_core/audit/privacy.py +101 -0
  15. devtorch_core/audit/scrubber.py +149 -0
  16. devtorch_core/audit/service.py +67 -0
  17. devtorch_core/audit/signing.py +127 -0
  18. devtorch_core/broadcast/__init__.py +4 -0
  19. devtorch_core/broadcast/broadcaster.py +100 -0
  20. devtorch_core/broadcast/watcher.py +71 -0
  21. devtorch_core/capability.py +639 -0
  22. devtorch_core/cloud/__init__.py +1 -0
  23. devtorch_core/cloud/client_config.py +472 -0
  24. devtorch_core/cloud/client_configs/.claude-opencode-fallback.json +8 -0
  25. devtorch_core/cloud/client_configs/.claude-stdio.json +13 -0
  26. devtorch_core/cloud/client_configs/.cursor-mcp.json +13 -0
  27. devtorch_core/cloud/client_configs/.opencode-bridge.json +13 -0
  28. devtorch_core/cloud/client_configs/.opencode.json +15 -0
  29. devtorch_core/cloud/client_configs/.vscode-mcp.json +13 -0
  30. devtorch_core/cloud/devtorch-mcp-bridge.js +357 -0
  31. devtorch_core/cloud/mcp_client.py +229 -0
  32. devtorch_core/cloud/setup.py +144 -0
  33. devtorch_core/cloud/sync.py +143 -0
  34. devtorch_core/cloud/sync_bundle.py +603 -0
  35. devtorch_core/cloud/sync_conflicts.py +159 -0
  36. devtorch_core/cloud/sync_state.py +159 -0
  37. devtorch_core/cloud/team_sync.py +283 -0
  38. devtorch_core/codex/__init__.py +9 -0
  39. devtorch_core/codex/__main__.py +97 -0
  40. devtorch_core/codex/capture.py +208 -0
  41. devtorch_core/codex/proxy.py +412 -0
  42. devtorch_core/concept_catalog.py +209 -0
  43. devtorch_core/consolidation/__init__.py +3 -0
  44. devtorch_core/consolidation/synthesizer.py +87 -0
  45. devtorch_core/consolidation/workflow.py +175 -0
  46. devtorch_core/daemon/__init__.py +27 -0
  47. devtorch_core/daemon/supervisor.py +293 -0
  48. devtorch_core/daemon/watcher.py +244 -0
  49. devtorch_core/dashboard_api.py +2012 -0
  50. devtorch_core/deltaf.py +97 -0
  51. devtorch_core/disclosure.py +50 -0
  52. devtorch_core/divergence/__init__.py +3 -0
  53. devtorch_core/divergence/detector.py +166 -0
  54. devtorch_core/gateway/__init__.py +32 -0
  55. devtorch_core/gateway/key_manager.py +124 -0
  56. devtorch_core/gateway/metrics_webhook.py +252 -0
  57. devtorch_core/gateway/policy.py +262 -0
  58. devtorch_core/gateway/server.py +727 -0
  59. devtorch_core/gateway/sso.py +233 -0
  60. devtorch_core/gcc.py +1246 -0
  61. devtorch_core/github/__init__.py +35 -0
  62. devtorch_core/github/app.py +240 -0
  63. devtorch_core/github/comment_builder.py +113 -0
  64. devtorch_core/github/pat.py +76 -0
  65. devtorch_core/github/pr_parser.py +82 -0
  66. devtorch_core/github/pr_reporter.py +555 -0
  67. devtorch_core/gitlab/__init__.py +177 -0
  68. devtorch_core/hitl/__init__.py +4 -0
  69. devtorch_core/hitl/channels.py +129 -0
  70. devtorch_core/hitl/orchestrator.py +95 -0
  71. devtorch_core/hooks/__init__.py +17 -0
  72. devtorch_core/hooks/claude_code.py +228 -0
  73. devtorch_core/hooks/git_capture.py +341 -0
  74. devtorch_core/hooks/git_commit.py +182 -0
  75. devtorch_core/hooks/installer.py +733 -0
  76. devtorch_core/hooks/pre_commit.py +157 -0
  77. devtorch_core/hooks/runner.py +344 -0
  78. devtorch_core/identity/__init__.py +4 -0
  79. devtorch_core/identity/agent.py +86 -0
  80. devtorch_core/identity/providers.py +85 -0
  81. devtorch_core/invariants.py +182 -0
  82. devtorch_core/mcp/__init__.py +10 -0
  83. devtorch_core/mcp/auth.py +177 -0
  84. devtorch_core/mcp/server.py +1049 -0
  85. devtorch_core/metrics/__init__.py +35 -0
  86. devtorch_core/metrics/aggregate.py +215 -0
  87. devtorch_core/metrics/calibrate.py +198 -0
  88. devtorch_core/metrics/calibration.py +125 -0
  89. devtorch_core/metrics/credibility.py +288 -0
  90. devtorch_core/metrics/delivery_time.py +70 -0
  91. devtorch_core/metrics/dhs.py +126 -0
  92. devtorch_core/metrics/mcs.py +96 -0
  93. devtorch_core/metrics/roi.py +88 -0
  94. devtorch_core/metrics/session_writer.py +81 -0
  95. devtorch_core/metrics/shadow_ai.py +117 -0
  96. devtorch_core/metrics/sprint_writer.py +243 -0
  97. devtorch_core/observability/__init__.py +78 -0
  98. devtorch_core/observability/datadog.py +157 -0
  99. devtorch_core/observability/formatter.py +119 -0
  100. devtorch_core/observability/report.py +264 -0
  101. devtorch_core/observability/servicenow.py +147 -0
  102. devtorch_core/observability/splunk.py +218 -0
  103. devtorch_core/observability/webhook.py +227 -0
  104. devtorch_core/parser/__init__.py +30 -0
  105. devtorch_core/parser/blocks.py +216 -0
  106. devtorch_core/parser/inference.py +159 -0
  107. devtorch_core/parser/thinking.py +112 -0
  108. devtorch_core/projects.py +169 -0
  109. devtorch_core/prompt_artifact.py +76 -0
  110. devtorch_core/proxy/__init__.py +9 -0
  111. devtorch_core/proxy/routes/__init__.py +1 -0
  112. devtorch_core/proxy/routes/anthropic.py +264 -0
  113. devtorch_core/proxy/routes/azure_openai.py +336 -0
  114. devtorch_core/proxy/routes/gemini.py +331 -0
  115. devtorch_core/proxy/routes/groq.py +284 -0
  116. devtorch_core/proxy/routes/ollama.py +279 -0
  117. devtorch_core/proxy/routes/openai.py +287 -0
  118. devtorch_core/proxy/server.py +356 -0
  119. devtorch_core/query/__init__.py +15 -0
  120. devtorch_core/query/grep.py +181 -0
  121. devtorch_core/query/hybrid.py +86 -0
  122. devtorch_core/query/semantic.py +157 -0
  123. devtorch_core/rdp.py +105 -0
  124. devtorch_core/reasoning/__init__.py +4 -0
  125. devtorch_core/reasoning/entry.py +31 -0
  126. devtorch_core/reasoning/store.py +122 -0
  127. devtorch_core/reasoning_plus/__init__.py +70 -0
  128. devtorch_core/reasoning_plus/augmenter.py +326 -0
  129. devtorch_core/reasoning_plus/capture.py +51 -0
  130. devtorch_core/reasoning_plus/config.py +256 -0
  131. devtorch_core/reasoning_plus/context.py +262 -0
  132. devtorch_core/reasoning_plus/learning/__init__.py +72 -0
  133. devtorch_core/reasoning_plus/learning/analytics.py +141 -0
  134. devtorch_core/reasoning_plus/learning/api.py +313 -0
  135. devtorch_core/reasoning_plus/learning/chain.py +285 -0
  136. devtorch_core/reasoning_plus/learning/composer.py +74 -0
  137. devtorch_core/reasoning_plus/learning/cross_project.py +234 -0
  138. devtorch_core/reasoning_plus/learning/embeddings.py +209 -0
  139. devtorch_core/reasoning_plus/learning/extractor.py +207 -0
  140. devtorch_core/reasoning_plus/learning/models.py +116 -0
  141. devtorch_core/reasoning_plus/learning/provenance.py +126 -0
  142. devtorch_core/reasoning_plus/learning/recorder.py +81 -0
  143. devtorch_core/reasoning_plus/learning/relevance.py +122 -0
  144. devtorch_core/reasoning_plus/learning/state.py +86 -0
  145. devtorch_core/reasoning_plus/learning/store.py +160 -0
  146. devtorch_core/reasoning_plus/learning/theta_learning_bridge.py +94 -0
  147. devtorch_core/reasoning_plus/prompt.py +90 -0
  148. devtorch_core/rep.py +134 -0
  149. devtorch_core/rep_network/__init__.py +25 -0
  150. devtorch_core/rep_network/merge.py +70 -0
  151. devtorch_core/rep_network/node.py +137 -0
  152. devtorch_core/rep_network/server.py +140 -0
  153. devtorch_core/rep_network/sync.py +207 -0
  154. devtorch_core/sensitivity.py +182 -0
  155. devtorch_core/serve.py +258 -0
  156. devtorch_core/session/__init__.py +39 -0
  157. devtorch_core/session/disagreement.py +188 -0
  158. devtorch_core/session/models.py +114 -0
  159. devtorch_core/session/orchestrator.py +182 -0
  160. devtorch_core/session/planner.py +169 -0
  161. devtorch_core/session/simulator.py +132 -0
  162. devtorch_core/signing.py +290 -0
  163. devtorch_core/sis.py +197 -0
  164. devtorch_core/storage.py +308 -0
  165. devtorch_core/templates/__init__.py +6 -0
  166. devtorch_core/templates/engine.py +122 -0
  167. devtorch_core/templates/go.py +18 -0
  168. devtorch_core/templates/infra.py +19 -0
  169. devtorch_core/templates/library/__init__.py +18 -0
  170. devtorch_core/templates/library/api_design.md +27 -0
  171. devtorch_core/templates/library/bug_fix.md +27 -0
  172. devtorch_core/templates/library/decision_record.md +27 -0
  173. devtorch_core/templates/library/engine.py +228 -0
  174. devtorch_core/templates/library/security_review.md +30 -0
  175. devtorch_core/templates/python.py +19 -0
  176. devtorch_core/templates/react.py +18 -0
  177. devtorch_core/templates/typescript.py +18 -0
  178. devtorch_core/theta.py +221 -0
  179. devtorch_core/theta_synthesis.py +268 -0
  180. devtorch_core/topics.py +320 -0
  181. devtorch_core/variance.py +219 -0
  182. devtorch_core/wrapper/__init__.py +52 -0
  183. devtorch_core/wrapper/anthropic.py +487 -0
  184. devtorch_core/wrapper/base.py +562 -0
  185. devtorch_core/wrapper/bedrock.py +342 -0
  186. devtorch_core/wrapper/gemini.py +422 -0
  187. devtorch_core/wrapper/ollama.py +527 -0
  188. devtorch_core/wrapper/openai.py +461 -0
  189. devtorch_core-3.0.1.dist-info/METADATA +867 -0
  190. devtorch_core-3.0.1.dist-info/RECORD +193 -0
  191. devtorch_core-3.0.1.dist-info/WHEEL +5 -0
  192. devtorch_core-3.0.1.dist-info/entry_points.txt +2 -0
  193. devtorch_core-3.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,342 @@
1
+ """
2
+ devtorch_core.wrapper.bedrock
3
+ ==============================
4
+ Stub wrapper around the AWS Bedrock ``boto3`` client.
5
+
6
+ Intercepts ``invoke_model`` for Anthropic Claude models served via Bedrock
7
+ (body format: ``{"anthropic_version": ..., "messages": [...], "system": ...}``).
8
+
9
+ Other model families (Titan, Llama, Mistral, etc.) pass through unchanged
10
+ with a debug log.
11
+
12
+ Usage
13
+ -----
14
+ ::
15
+
16
+ from devtorch_core.wrapper.bedrock import DevTorchBedrock
17
+
18
+ client = DevTorchBedrock(region_name="us-east-1")
19
+ response = client.invoke_model(
20
+ modelId="anthropic.claude-opus-4-5-20240229-v1:0",
21
+ body=json.dumps({
22
+ "anthropic_version": "bedrock-2023-05-31",
23
+ "max_tokens": 1024,
24
+ "messages": [{"role": "user", "content": "Hello"}],
25
+ }),
26
+ contentType="application/json",
27
+ accept="application/json",
28
+ )
29
+ """
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ import logging
34
+ import uuid
35
+ from typing import Any
36
+
37
+ from .base import CaptureOrchestrator, RACPInjector, _reasoning_text, find_gcc_repo, is_disabled
38
+
39
+ logger = logging.getLogger("devtorch.wrapper.bedrock")
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Optional import
43
+ # ---------------------------------------------------------------------------
44
+
45
+ try:
46
+ import boto3 as _boto3 # type: ignore[import]
47
+
48
+ HAS_BOTO3 = True
49
+ except ImportError:
50
+ HAS_BOTO3 = False
51
+ _boto3 = None # type: ignore[assignment]
52
+
53
+ # Model ID prefixes that use the Anthropic Claude Bedrock body format
54
+ _ANTHROPIC_BEDROCK_PREFIXES = (
55
+ "anthropic.claude",
56
+ "us.anthropic.claude",
57
+ "eu.anthropic.claude",
58
+ )
59
+
60
+
61
+ # ---------------------------------------------------------------------------
62
+ # Wrapper
63
+ # ---------------------------------------------------------------------------
64
+
65
+
66
+ class DevTorchBedrock:
67
+ """
68
+ Wrapper around a ``boto3`` Bedrock runtime client.
69
+
70
+ Intercepts ``invoke_model`` for Anthropic Claude models to inject RACP
71
+ context and capture reasoning. All other calls are passed through
72
+ unchanged. Degrades gracefully if boto3 is not installed or .GCC/ is
73
+ missing.
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ *args: Any,
79
+ gcc_repo: Any = None,
80
+ service_name: str = "bedrock-runtime",
81
+ **kwargs: Any,
82
+ ) -> None:
83
+ if not HAS_BOTO3:
84
+ raise ImportError(
85
+ "boto3 package not installed. "
86
+ "pip install devtorch[wrapper]"
87
+ )
88
+ self._client = _boto3.client(service_name, *args, **kwargs) # type: ignore[union-attr]
89
+ self._gcc = gcc_repo or find_gcc_repo()
90
+ self._injector = RACPInjector(self._gcc) if self._gcc else None
91
+ self._capture = CaptureOrchestrator(self._gcc) if self._gcc else None
92
+
93
+ # ------------------------------------------------------------------
94
+ # Intercepted method
95
+ # ------------------------------------------------------------------
96
+
97
+ def invoke_model(self, *, modelId: str, body: Any, **kwargs: Any) -> Any:
98
+ """
99
+ Intercept invoke_model for Claude-via-Bedrock calls.
100
+
101
+ 1. Decode body JSON.
102
+ 2. If Anthropic Claude model: inject RACP into ``system`` field.
103
+ 3. Call real client.
104
+ 4. Decode response body and capture.
105
+ 5. Return raw boto3 response (stream-compatible).
106
+ """
107
+ session_id = str(uuid.uuid4())[:8]
108
+ call_id = str(uuid.uuid4())
109
+ is_anthropic = any(
110
+ modelId.startswith(pfx) for pfx in _ANTHROPIC_BEDROCK_PREFIXES
111
+ )
112
+
113
+ # Parse body for prompt text and possible injection
114
+ body_dict = self._parse_body(body)
115
+ prompt_text = _extract_prompt_text(body_dict.get("messages", []))
116
+
117
+ if is_anthropic and not is_disabled():
118
+ body = self._inject_anthropic(body_dict, prompt_text, session_id, call_id, modelId)
119
+ elif not is_anthropic:
120
+ logger.debug(
121
+ "devtorch: Bedrock model %s is not Anthropic Claude — "
122
+ "passing through without RACP injection",
123
+ modelId,
124
+ )
125
+
126
+ # Call the real Bedrock client
127
+ try:
128
+ response = self._client.invoke_model(modelId=modelId, body=body, **kwargs)
129
+ except Exception:
130
+ self._record_llm_outcome(
131
+ prompt_text=prompt_text,
132
+ response_text="",
133
+ thinking_blocks=[],
134
+ session_id=session_id,
135
+ call_id=call_id,
136
+ model_name=modelId,
137
+ outcome="failure",
138
+ )
139
+ raise
140
+
141
+ # Capture response for Anthropic models
142
+ if is_anthropic:
143
+ text, thinking_blocks = self._capture_response(response, session_id)
144
+ self._record_llm_outcome(
145
+ prompt_text=prompt_text,
146
+ response_text=text,
147
+ thinking_blocks=thinking_blocks,
148
+ session_id=session_id,
149
+ call_id=call_id,
150
+ model_name=modelId,
151
+ outcome="success",
152
+ )
153
+
154
+ return response
155
+
156
+ # ------------------------------------------------------------------
157
+ # Injection
158
+ # ------------------------------------------------------------------
159
+
160
+ def _parse_body(self, body: Any) -> dict:
161
+ """Best-effort parse of the Bedrock request body into a dict."""
162
+ try:
163
+ if isinstance(body, (bytes, bytearray)):
164
+ return json.loads(body.decode("utf-8"))
165
+ elif isinstance(body, str):
166
+ return json.loads(body)
167
+ elif isinstance(body, dict):
168
+ return dict(body)
169
+ except Exception:
170
+ pass
171
+ return {}
172
+
173
+ def _inject_anthropic(
174
+ self,
175
+ body_dict: dict,
176
+ prompt_text: str = "",
177
+ session_id: str = "",
178
+ call_id: str = "",
179
+ model_name: str = "",
180
+ ) -> str:
181
+ """Inject RACP prefix into the ``system`` field of an Anthropic body."""
182
+ if not self._injector:
183
+ return json.dumps(body_dict)
184
+
185
+ try:
186
+ prefix, _ = self._injector.build_system_prefix_with_provenance(
187
+ prompt_text=prompt_text or None,
188
+ session_id=session_id,
189
+ call_id=call_id,
190
+ model_name=model_name,
191
+ )
192
+ if prefix:
193
+ existing_system = body_dict.get("system", "")
194
+ body_dict["system"] = (
195
+ (prefix + "\n\n" + existing_system).strip()
196
+ if existing_system
197
+ else prefix
198
+ )
199
+
200
+ return json.dumps(body_dict)
201
+ except Exception as exc:
202
+ logger.warning("devtorch: Bedrock RACP injection failed — %s", exc)
203
+ return json.dumps(body_dict)
204
+
205
+ # ------------------------------------------------------------------
206
+ # Capture
207
+ # ------------------------------------------------------------------
208
+
209
+ def _capture_response(self, response: Any, session_id: str) -> tuple[str, list]:
210
+ text = ""
211
+ thinking: list = []
212
+ if not self._capture or is_disabled():
213
+ return text, thinking
214
+ try:
215
+ # Read and re-inject body stream
216
+ body_stream = response.get("body")
217
+ if body_stream is None:
218
+ return text, thinking
219
+ raw = body_stream.read()
220
+ response_dict = json.loads(raw.decode("utf-8"))
221
+
222
+ # Extract text from Anthropic Bedrock response format
223
+ for block in response_dict.get("content", []):
224
+ if isinstance(block, dict) and block.get("type") == "text":
225
+ text += block.get("text", "")
226
+
227
+ # Extract thinking blocks
228
+ try:
229
+ from devtorch_core.parser.thinking import extract_thinking_blocks
230
+
231
+ thinking = extract_thinking_blocks(response_dict)
232
+ except Exception:
233
+ pass
234
+
235
+ self._capture.capture(text, thinking, session_id)
236
+
237
+ # Strip inline <thinking> blocks from the response body before re-wrapping
238
+ try:
239
+ from devtorch_core.reasoning_plus.capture import strip_inline_thinking
240
+
241
+ for block in response_dict.get("content", []):
242
+ if isinstance(block, dict) and block.get("type") == "text":
243
+ block["text"] = strip_inline_thinking(block.get("text", ""))
244
+ raw = json.dumps(response_dict).encode("utf-8")
245
+ except Exception as exc:
246
+ logger.warning("devtorch: Bedrock strip thinking failed — %s", exc)
247
+
248
+ # Wrap body bytes back into a StreamingBody-compatible object
249
+ # so the caller can still read the response
250
+ import io
251
+
252
+ response["body"] = _BytesBodyWrapper(raw)
253
+ except Exception as exc:
254
+ logger.warning("devtorch: Bedrock _capture_response failed — %s", exc)
255
+ return text, thinking
256
+
257
+ def _record_llm_outcome(
258
+ self,
259
+ *,
260
+ prompt_text: str,
261
+ response_text: str,
262
+ thinking_blocks: list,
263
+ session_id: str,
264
+ call_id: str,
265
+ model_name: str,
266
+ outcome: str,
267
+ ) -> None:
268
+ if not self._capture or is_disabled():
269
+ return
270
+ try:
271
+ reasoning = _reasoning_text(thinking_blocks)
272
+ self._capture.record_learning_call(
273
+ call_type="llm",
274
+ reasoning=reasoning,
275
+ session_id=session_id,
276
+ call_id=call_id,
277
+ name=model_name,
278
+ input=prompt_text,
279
+ output=response_text,
280
+ outcome=outcome,
281
+ )
282
+ except Exception as exc:
283
+ logger.warning("devtorch: _record_llm_outcome failed — %s", exc)
284
+
285
+ # ------------------------------------------------------------------
286
+ # Proxy everything else to the underlying boto3 client
287
+ # ------------------------------------------------------------------
288
+
289
+ def __getattr__(self, name: str) -> Any:
290
+ return getattr(self._client, name)
291
+
292
+
293
+ # ---------------------------------------------------------------------------
294
+ # Internal helpers
295
+ # ---------------------------------------------------------------------------
296
+
297
+
298
+ def _extract_prompt_text(messages: list) -> str:
299
+ """Best-effort concatenation of user-visible message text for smart context."""
300
+ parts: list[str] = []
301
+ for m in messages or []:
302
+ if isinstance(m, dict):
303
+ role = m.get("role", "")
304
+ content = m.get("content", "")
305
+ if role in ("user", "system") and content:
306
+ if isinstance(content, str):
307
+ parts.append(content)
308
+ elif isinstance(content, list):
309
+ for item in content:
310
+ if isinstance(item, dict) and item.get("type") == "text":
311
+ parts.append(item.get("text", ""))
312
+ elif isinstance(item, str):
313
+ parts.append(item)
314
+ elif hasattr(m, "role") and hasattr(m, "content"):
315
+ role = getattr(m, "role", "")
316
+ content = getattr(m, "content", "")
317
+ if role in ("user", "system") and content:
318
+ parts.append(str(content))
319
+ return "\n".join(parts)[:4000]
320
+
321
+
322
+ # ---------------------------------------------------------------------------
323
+ # Helper: re-wrap consumed body bytes
324
+ # ---------------------------------------------------------------------------
325
+
326
+
327
+ class _BytesBodyWrapper:
328
+ """
329
+ Minimal wrapper that restores a boto3-style StreamingBody after we've
330
+ read all bytes for capture.
331
+ """
332
+
333
+ def __init__(self, data: bytes) -> None:
334
+ import io
335
+
336
+ self._stream = io.BytesIO(data)
337
+
338
+ def read(self, amt: int = -1) -> bytes: # type: ignore[override]
339
+ return self._stream.read() if amt == -1 else self._stream.read(amt)
340
+
341
+ def __iter__(self) -> Any:
342
+ return iter(self._stream)