htmlgraph 0.26.25__py3-none-any.whl → 0.27.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 (175) hide show
  1. htmlgraph/__init__.py +23 -1
  2. htmlgraph/__init__.pyi +123 -0
  3. htmlgraph/agent_registry.py +2 -1
  4. htmlgraph/analytics/cli.py +3 -3
  5. htmlgraph/analytics/cost_analyzer.py +5 -1
  6. htmlgraph/analytics/cost_monitor.py +664 -0
  7. htmlgraph/analytics/cross_session.py +13 -9
  8. htmlgraph/analytics/dependency.py +10 -6
  9. htmlgraph/analytics/strategic/__init__.py +80 -0
  10. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  11. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  12. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  13. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  14. htmlgraph/analytics/work_type.py +15 -11
  15. htmlgraph/analytics_index.py +2 -1
  16. htmlgraph/api/cost_alerts_websocket.py +416 -0
  17. htmlgraph/api/main.py +167 -62
  18. htmlgraph/api/websocket.py +538 -0
  19. htmlgraph/attribute_index.py +2 -1
  20. htmlgraph/builders/base.py +2 -1
  21. htmlgraph/builders/bug.py +2 -1
  22. htmlgraph/builders/chore.py +2 -1
  23. htmlgraph/builders/epic.py +2 -1
  24. htmlgraph/builders/feature.py +2 -1
  25. htmlgraph/builders/insight.py +2 -1
  26. htmlgraph/builders/metric.py +2 -1
  27. htmlgraph/builders/pattern.py +2 -1
  28. htmlgraph/builders/phase.py +2 -1
  29. htmlgraph/builders/spike.py +2 -1
  30. htmlgraph/builders/track.py +2 -1
  31. htmlgraph/cli/analytics.py +2 -1
  32. htmlgraph/cli/base.py +2 -1
  33. htmlgraph/cli/core.py +2 -1
  34. htmlgraph/cli/main.py +2 -1
  35. htmlgraph/cli/models.py +2 -1
  36. htmlgraph/cli/templates/cost_dashboard.py +2 -1
  37. htmlgraph/cli/work/__init__.py +2 -1
  38. htmlgraph/cli/work/browse.py +2 -1
  39. htmlgraph/cli/work/features.py +2 -1
  40. htmlgraph/cli/work/orchestration.py +2 -1
  41. htmlgraph/cli/work/report.py +2 -1
  42. htmlgraph/cli/work/sessions.py +2 -1
  43. htmlgraph/cli/work/snapshot.py +2 -1
  44. htmlgraph/cli/work/tracks.py +2 -1
  45. htmlgraph/collections/base.py +10 -5
  46. htmlgraph/collections/bug.py +2 -1
  47. htmlgraph/collections/chore.py +2 -1
  48. htmlgraph/collections/epic.py +2 -1
  49. htmlgraph/collections/feature.py +2 -1
  50. htmlgraph/collections/insight.py +2 -1
  51. htmlgraph/collections/metric.py +2 -1
  52. htmlgraph/collections/pattern.py +2 -1
  53. htmlgraph/collections/phase.py +2 -1
  54. htmlgraph/collections/session.py +12 -7
  55. htmlgraph/collections/spike.py +6 -1
  56. htmlgraph/collections/task_delegation.py +7 -2
  57. htmlgraph/collections/todo.py +2 -1
  58. htmlgraph/collections/traces.py +15 -10
  59. htmlgraph/config/cost_models.json +56 -0
  60. htmlgraph/context_analytics.py +2 -1
  61. htmlgraph/db/schema.py +67 -6
  62. htmlgraph/dependency_models.py +2 -1
  63. htmlgraph/edge_index.py +2 -1
  64. htmlgraph/event_log.py +83 -64
  65. htmlgraph/event_migration.py +2 -1
  66. htmlgraph/file_watcher.py +12 -8
  67. htmlgraph/find_api.py +2 -1
  68. htmlgraph/git_events.py +6 -2
  69. htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
  70. htmlgraph/hooks/drift_handler.py +3 -3
  71. htmlgraph/hooks/event_tracker.py +40 -61
  72. htmlgraph/hooks/installer.py +5 -1
  73. htmlgraph/hooks/orchestrator.py +4 -0
  74. htmlgraph/hooks/orchestrator_reflector.py +4 -0
  75. htmlgraph/hooks/post_tool_use_failure.py +7 -3
  76. htmlgraph/hooks/posttooluse.py +4 -0
  77. htmlgraph/hooks/prompt_analyzer.py +5 -5
  78. htmlgraph/hooks/session_handler.py +2 -1
  79. htmlgraph/hooks/session_summary.py +6 -2
  80. htmlgraph/hooks/validator.py +8 -4
  81. htmlgraph/ids.py +2 -1
  82. htmlgraph/learning.py +2 -1
  83. htmlgraph/mcp_server.py +2 -1
  84. htmlgraph/operations/analytics.py +2 -1
  85. htmlgraph/operations/bootstrap.py +2 -1
  86. htmlgraph/operations/events.py +2 -1
  87. htmlgraph/operations/fastapi_server.py +2 -1
  88. htmlgraph/operations/hooks.py +2 -1
  89. htmlgraph/operations/initialization.py +2 -1
  90. htmlgraph/operations/server.py +2 -1
  91. htmlgraph/orchestration/claude_launcher.py +23 -20
  92. htmlgraph/orchestration/command_builder.py +2 -1
  93. htmlgraph/orchestration/headless_spawner.py +6 -2
  94. htmlgraph/orchestration/model_selection.py +7 -3
  95. htmlgraph/orchestration/plugin_manager.py +24 -19
  96. htmlgraph/orchestration/spawners/claude.py +5 -2
  97. htmlgraph/orchestration/spawners/codex.py +12 -19
  98. htmlgraph/orchestration/spawners/copilot.py +13 -18
  99. htmlgraph/orchestration/spawners/gemini.py +12 -19
  100. htmlgraph/orchestration/subprocess_runner.py +6 -3
  101. htmlgraph/orchestration/task_coordination.py +16 -8
  102. htmlgraph/orchestrator.py +2 -1
  103. htmlgraph/parallel.py +2 -1
  104. htmlgraph/query_builder.py +2 -1
  105. htmlgraph/reflection.py +2 -1
  106. htmlgraph/refs.py +2 -1
  107. htmlgraph/repo_hash.py +2 -1
  108. htmlgraph/repositories/__init__.py +292 -0
  109. htmlgraph/repositories/analytics_repository.py +455 -0
  110. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  111. htmlgraph/repositories/feature_repository.py +581 -0
  112. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  113. htmlgraph/repositories/feature_repository_memory.py +607 -0
  114. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  115. htmlgraph/repositories/filter_service.py +620 -0
  116. htmlgraph/repositories/filter_service_standard.py +445 -0
  117. htmlgraph/repositories/shared_cache.py +621 -0
  118. htmlgraph/repositories/shared_cache_memory.py +395 -0
  119. htmlgraph/repositories/track_repository.py +552 -0
  120. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  121. htmlgraph/repositories/track_repository_memory.py +508 -0
  122. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  123. htmlgraph/sdk/__init__.py +398 -0
  124. htmlgraph/sdk/__init__.pyi +14 -0
  125. htmlgraph/sdk/analytics/__init__.py +19 -0
  126. htmlgraph/sdk/analytics/engine.py +155 -0
  127. htmlgraph/sdk/analytics/helpers.py +178 -0
  128. htmlgraph/sdk/analytics/registry.py +109 -0
  129. htmlgraph/sdk/base.py +484 -0
  130. htmlgraph/sdk/constants.py +216 -0
  131. htmlgraph/sdk/core.pyi +308 -0
  132. htmlgraph/sdk/discovery.py +120 -0
  133. htmlgraph/sdk/help/__init__.py +12 -0
  134. htmlgraph/sdk/help/mixin.py +699 -0
  135. htmlgraph/sdk/mixins/__init__.py +15 -0
  136. htmlgraph/sdk/mixins/attribution.py +113 -0
  137. htmlgraph/sdk/mixins/mixin.py +410 -0
  138. htmlgraph/sdk/operations/__init__.py +12 -0
  139. htmlgraph/sdk/operations/mixin.py +427 -0
  140. htmlgraph/sdk/orchestration/__init__.py +17 -0
  141. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  142. htmlgraph/sdk/orchestration/spawner.py +204 -0
  143. htmlgraph/sdk/planning/__init__.py +19 -0
  144. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  145. htmlgraph/sdk/planning/mixin.py +211 -0
  146. htmlgraph/sdk/planning/parallel.py +186 -0
  147. htmlgraph/sdk/planning/queue.py +210 -0
  148. htmlgraph/sdk/planning/recommendations.py +87 -0
  149. htmlgraph/sdk/planning/smart_planning.py +319 -0
  150. htmlgraph/sdk/session/__init__.py +19 -0
  151. htmlgraph/sdk/session/continuity.py +57 -0
  152. htmlgraph/sdk/session/handoff.py +110 -0
  153. htmlgraph/sdk/session/info.py +309 -0
  154. htmlgraph/sdk/session/manager.py +103 -0
  155. htmlgraph/sdk/strategic/__init__.py +26 -0
  156. htmlgraph/sdk/strategic/mixin.py +563 -0
  157. htmlgraph/server.py +21 -17
  158. htmlgraph/session_warning.py +2 -1
  159. htmlgraph/sessions/handoff.py +4 -3
  160. htmlgraph/system_prompts.py +2 -1
  161. htmlgraph/track_builder.py +2 -1
  162. htmlgraph/transcript.py +2 -1
  163. htmlgraph/watch.py +2 -1
  164. htmlgraph/work_type_utils.py +2 -1
  165. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/METADATA +1 -1
  166. htmlgraph-0.27.1.dist-info/RECORD +332 -0
  167. htmlgraph/sdk.py +0 -3500
  168. htmlgraph-0.26.25.dist-info/RECORD +0 -274
  169. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/dashboard.html +0 -0
  170. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/styles.css +0 -0
  171. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  172. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  173. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  174. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/WHEEL +0 -0
  175. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,563 @@
1
+ """
2
+ Strategic Analytics Mixin for SDK
3
+
4
+ Provides SDK methods for accessing Phase 3 Strategic Analytics:
5
+ - Pattern detection (tool sequences, delegation chains, error patterns)
6
+ - Suggestion generation (next actions, delegations, model selection)
7
+ - Preference management (feedback, learning, personalization)
8
+ - Cost optimization (token budgets, parallelization, model selection)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from dataclasses import dataclass
15
+ from pathlib import Path
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ if TYPE_CHECKING:
19
+ from htmlgraph.sdk import SDK
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ @dataclass
25
+ class StrategicAnalyticsInterface:
26
+ """
27
+ Interface for strategic analytics operations.
28
+
29
+ Provides access to:
30
+ - PatternDetector for pattern detection
31
+ - SuggestionEngine for generating suggestions
32
+ - PreferenceManager for preference learning
33
+ - CostOptimizer for cost optimization
34
+
35
+ This interface is exposed as sdk.strategic on the SDK.
36
+ """
37
+
38
+ _sdk: SDK
39
+ _db_path: Path | None = None
40
+
41
+ def __post_init__(self) -> None:
42
+ """Initialize database path."""
43
+ from htmlgraph.config import get_database_path
44
+
45
+ self._db_path = get_database_path()
46
+
47
+ # ===== Pattern Detection =====
48
+
49
+ def detect_patterns(
50
+ self,
51
+ min_frequency: int = 3,
52
+ days_back: int = 30,
53
+ ) -> list[dict[str, Any]]:
54
+ """
55
+ Detect all patterns from event history.
56
+
57
+ Analyzes tool sequences, delegation chains, and error patterns
58
+ to identify successful workflows.
59
+
60
+ Args:
61
+ min_frequency: Minimum occurrences to be considered a pattern
62
+ days_back: Number of days of history to analyze
63
+
64
+ Returns:
65
+ List of pattern dictionaries sorted by confidence
66
+
67
+ Example:
68
+ >>> sdk = SDK(agent="claude")
69
+ >>> patterns = sdk.strategic.detect_patterns()
70
+ >>> for p in patterns[:5]:
71
+ ... print(f"{p['pattern_type']}: {p['confidence']:.0%}")
72
+ """
73
+ try:
74
+ from htmlgraph.analytics.strategic import PatternDetector
75
+
76
+ detector = PatternDetector(self._db_path)
77
+ patterns = detector.detect_all_patterns(
78
+ min_frequency=min_frequency,
79
+ days_back=days_back,
80
+ )
81
+ detector.close()
82
+
83
+ return [p.to_dict() for p in patterns]
84
+ except ImportError:
85
+ logger.warning("Strategic analytics not available")
86
+ return []
87
+ except Exception as e:
88
+ logger.error(f"Error detecting patterns: {e}")
89
+ return []
90
+
91
+ def detect_tool_sequences(
92
+ self,
93
+ window_size: int = 3,
94
+ min_frequency: int = 3,
95
+ ) -> list[dict[str, Any]]:
96
+ """
97
+ Detect common tool call sequence patterns.
98
+
99
+ Args:
100
+ window_size: Number of consecutive tools in each sequence
101
+ min_frequency: Minimum occurrences to be considered a pattern
102
+
103
+ Returns:
104
+ List of tool sequence patterns
105
+
106
+ Example:
107
+ >>> sequences = sdk.strategic.detect_tool_sequences()
108
+ >>> for seq in sequences[:3]:
109
+ ... print(f"Sequence: {' -> '.join(seq['sequence'])}")
110
+ """
111
+ try:
112
+ from htmlgraph.analytics.strategic import PatternDetector
113
+
114
+ detector = PatternDetector(self._db_path)
115
+ patterns = detector.detect_tool_sequences(
116
+ window_size=window_size,
117
+ min_frequency=min_frequency,
118
+ )
119
+ detector.close()
120
+
121
+ return [p.to_dict() for p in patterns]
122
+ except Exception as e:
123
+ logger.error(f"Error detecting tool sequences: {e}")
124
+ return []
125
+
126
+ def detect_delegation_chains(
127
+ self,
128
+ min_frequency: int = 2,
129
+ ) -> list[dict[str, Any]]:
130
+ """
131
+ Detect common delegation chain patterns.
132
+
133
+ Identifies which agent combinations work well together.
134
+
135
+ Args:
136
+ min_frequency: Minimum occurrences to be considered a pattern
137
+
138
+ Returns:
139
+ List of delegation chain patterns
140
+
141
+ Example:
142
+ >>> chains = sdk.strategic.detect_delegation_chains()
143
+ >>> for chain in chains:
144
+ ... print(f"Chain: {' -> '.join(chain['agents'])}")
145
+ """
146
+ try:
147
+ from htmlgraph.analytics.strategic import PatternDetector
148
+
149
+ detector = PatternDetector(self._db_path)
150
+ patterns = detector.detect_delegation_chains(min_frequency=min_frequency)
151
+ detector.close()
152
+
153
+ return [p.to_dict() for p in patterns]
154
+ except Exception as e:
155
+ logger.error(f"Error detecting delegation chains: {e}")
156
+ return []
157
+
158
+ # ===== Suggestion Engine =====
159
+
160
+ def get_suggestions(
161
+ self,
162
+ task_description: str = "",
163
+ max_suggestions: int = 5,
164
+ ) -> list[dict[str, Any]]:
165
+ """
166
+ Get suggestions based on current context and learned patterns.
167
+
168
+ Args:
169
+ task_description: Current task (optional, improves suggestions)
170
+ max_suggestions: Maximum number of suggestions
171
+
172
+ Returns:
173
+ List of suggestion dictionaries sorted by score
174
+
175
+ Example:
176
+ >>> suggestions = sdk.strategic.get_suggestions(
177
+ ... task_description="Implement user authentication"
178
+ ... )
179
+ >>> for s in suggestions:
180
+ ... print(f"{s['title']} ({s['confidence']:.0%})")
181
+ """
182
+ try:
183
+ from htmlgraph.analytics.strategic import SuggestionEngine
184
+ from htmlgraph.analytics.strategic.suggestion_engine import TaskContext
185
+
186
+ engine = SuggestionEngine(self._db_path)
187
+
188
+ context = TaskContext(
189
+ current_task=task_description,
190
+ agent_type=self._sdk._agent_id or "orchestrator",
191
+ )
192
+
193
+ suggestions = engine.suggest(context, max_suggestions=max_suggestions)
194
+ engine.close()
195
+
196
+ return [s.to_dict() for s in suggestions]
197
+ except Exception as e:
198
+ logger.error(f"Error getting suggestions: {e}")
199
+ return []
200
+
201
+ def generate_task_code(self, pattern_id: str) -> str | None:
202
+ """
203
+ Generate Task() code from a detected pattern.
204
+
205
+ Args:
206
+ pattern_id: ID of pattern to generate code from
207
+
208
+ Returns:
209
+ Python code snippet or None if pattern not found
210
+
211
+ Example:
212
+ >>> code = sdk.strategic.generate_task_code("tsp-abc123")
213
+ >>> print(code)
214
+ """
215
+ try:
216
+ from htmlgraph.analytics.strategic import PatternDetector, SuggestionEngine
217
+
218
+ detector = PatternDetector(self._db_path)
219
+ engine = SuggestionEngine(self._db_path)
220
+
221
+ pattern = detector.get_pattern_by_id(pattern_id)
222
+ if pattern:
223
+ code = engine.generate_task_code(pattern)
224
+ detector.close()
225
+ engine.close()
226
+ return code
227
+
228
+ detector.close()
229
+ engine.close()
230
+ return None
231
+ except Exception as e:
232
+ logger.error(f"Error generating task code: {e}")
233
+ return None
234
+
235
+ # ===== Preference Management =====
236
+
237
+ def get_preferences(self, user_id: str = "default") -> dict[str, Any]:
238
+ """
239
+ Get learned preferences for a user.
240
+
241
+ Args:
242
+ user_id: User identifier
243
+
244
+ Returns:
245
+ Dictionary of preference settings
246
+
247
+ Example:
248
+ >>> prefs = sdk.strategic.get_preferences()
249
+ >>> print(f"Model preferences: {prefs['model_preferences']}")
250
+ """
251
+ try:
252
+ from htmlgraph.analytics.strategic import PreferenceManager
253
+
254
+ manager = PreferenceManager(self._db_path)
255
+ prefs = manager.get_preferences(user_id)
256
+ manager.close()
257
+
258
+ return prefs.to_dict()
259
+ except Exception as e:
260
+ logger.error(f"Error getting preferences: {e}")
261
+ return {}
262
+
263
+ def record_feedback(
264
+ self,
265
+ suggestion_id: str,
266
+ accepted: bool,
267
+ outcome: str = "unknown",
268
+ comment: str | None = None,
269
+ ) -> str | None:
270
+ """
271
+ Record feedback on a suggestion.
272
+
273
+ This enables the system to learn from user decisions.
274
+
275
+ Args:
276
+ suggestion_id: ID of the suggestion
277
+ accepted: Whether user accepted the suggestion
278
+ outcome: Result of following suggestion (successful, failed, partial)
279
+ comment: Optional text comment
280
+
281
+ Returns:
282
+ Feedback ID if successful, None otherwise
283
+
284
+ Example:
285
+ >>> sdk.strategic.record_feedback(
286
+ ... suggestion_id="sug-abc123",
287
+ ... accepted=True,
288
+ ... outcome="successful"
289
+ ... )
290
+ """
291
+ try:
292
+ from htmlgraph.analytics.strategic import PreferenceManager
293
+
294
+ manager = PreferenceManager(self._db_path)
295
+ feedback_id = manager.record_feedback(
296
+ suggestion_id=suggestion_id,
297
+ accepted=accepted,
298
+ user_id="default",
299
+ session_id=self._sdk._parent_session or "cli-session",
300
+ outcome=outcome,
301
+ comment=comment,
302
+ )
303
+ manager.close()
304
+
305
+ return feedback_id
306
+ except Exception as e:
307
+ logger.error(f"Error recording feedback: {e}")
308
+ return None
309
+
310
+ def reset_preferences(self, user_id: str = "default") -> bool:
311
+ """
312
+ Reset preferences to defaults.
313
+
314
+ Args:
315
+ user_id: User to reset
316
+
317
+ Returns:
318
+ True if reset successfully
319
+
320
+ Example:
321
+ >>> sdk.strategic.reset_preferences()
322
+ """
323
+ try:
324
+ from htmlgraph.analytics.strategic import PreferenceManager
325
+
326
+ manager = PreferenceManager(self._db_path)
327
+ result = manager.reset_preferences(user_id)
328
+ manager.close()
329
+
330
+ return result
331
+ except Exception as e:
332
+ logger.error(f"Error resetting preferences: {e}")
333
+ return False
334
+
335
+ def get_acceptance_rate(
336
+ self,
337
+ suggestion_type: str | None = None,
338
+ ) -> float:
339
+ """
340
+ Get suggestion acceptance rate.
341
+
342
+ Args:
343
+ suggestion_type: Optional filter by type
344
+
345
+ Returns:
346
+ Acceptance rate as percentage (0-100)
347
+
348
+ Example:
349
+ >>> rate = sdk.strategic.get_acceptance_rate("delegation")
350
+ >>> print(f"Delegation suggestions: {rate:.0f}% accepted")
351
+ """
352
+ try:
353
+ from htmlgraph.analytics.strategic import PreferenceManager
354
+
355
+ manager = PreferenceManager(self._db_path)
356
+ rate = manager.get_acceptance_rate("default", suggestion_type)
357
+ manager.close()
358
+
359
+ return rate
360
+ except Exception as e:
361
+ logger.error(f"Error getting acceptance rate: {e}")
362
+ return 0.0
363
+
364
+ # ===== Cost Optimization =====
365
+
366
+ def suggest_token_budget(
367
+ self,
368
+ task_description: str,
369
+ tool_name: str | None = None,
370
+ ) -> dict[str, Any]:
371
+ """
372
+ Get token budget suggestion for a task.
373
+
374
+ Args:
375
+ task_description: Description of the task
376
+ tool_name: Specific tool being used (optional)
377
+
378
+ Returns:
379
+ Token budget recommendation
380
+
381
+ Example:
382
+ >>> budget = sdk.strategic.suggest_token_budget(
383
+ ... "Implement caching for API"
384
+ ... )
385
+ >>> print(f"Recommended: {budget['recommended']} tokens")
386
+ """
387
+ try:
388
+ from htmlgraph.analytics.strategic import CostOptimizer
389
+
390
+ optimizer = CostOptimizer(self._db_path)
391
+ budget = optimizer.suggest_token_budget(task_description, tool_name)
392
+ optimizer.close()
393
+
394
+ return budget.to_dict()
395
+ except Exception as e:
396
+ logger.error(f"Error suggesting token budget: {e}")
397
+ return {"recommended": 5000, "minimum": 2500, "maximum": 10000}
398
+
399
+ def choose_model(
400
+ self,
401
+ task_description: str,
402
+ budget_constraint: int | None = None,
403
+ ) -> dict[str, Any]:
404
+ """
405
+ Get model selection recommendation.
406
+
407
+ Args:
408
+ task_description: Description of the task
409
+ budget_constraint: Maximum token budget (optional)
410
+
411
+ Returns:
412
+ Model recommendation
413
+
414
+ Example:
415
+ >>> model = sdk.strategic.choose_model(
416
+ ... "Refactor authentication system"
417
+ ... )
418
+ >>> print(f"Recommended: {model['recommended_model']}")
419
+ """
420
+ try:
421
+ from htmlgraph.analytics.strategic import CostOptimizer
422
+
423
+ optimizer = CostOptimizer(self._db_path)
424
+ recommendation = optimizer.choose_model(task_description, budget_constraint)
425
+ optimizer.close()
426
+
427
+ return recommendation.to_dict()
428
+ except Exception as e:
429
+ logger.error(f"Error choosing model: {e}")
430
+ return {"recommended_model": "sonnet", "confidence": 0.5}
431
+
432
+ def get_cost_summary(self, session_id: str | None = None) -> dict[str, Any]:
433
+ """
434
+ Get cost summary for a session.
435
+
436
+ Args:
437
+ session_id: Session to summarize (current if not specified)
438
+
439
+ Returns:
440
+ Cost breakdown by tool
441
+
442
+ Example:
443
+ >>> summary = sdk.strategic.get_cost_summary()
444
+ >>> print(f"Total tokens: {summary['total_tokens']}")
445
+ """
446
+ try:
447
+ from htmlgraph.analytics.strategic import CostOptimizer
448
+
449
+ optimizer = CostOptimizer(self._db_path)
450
+ session = session_id or self._sdk._parent_session or "cli-session"
451
+ summary = optimizer.get_cost_summary(session)
452
+ optimizer.close()
453
+
454
+ return summary
455
+ except Exception as e:
456
+ logger.error(f"Error getting cost summary: {e}")
457
+ return {"total_tokens": 0, "breakdown": []}
458
+
459
+ # ===== Task Decomposition =====
460
+
461
+ def suggest_task_decomposition(
462
+ self,
463
+ task_description: str,
464
+ max_subtasks: int = 5,
465
+ ) -> list[dict[str, Any]]:
466
+ """
467
+ Suggest task decomposition based on learned patterns.
468
+
469
+ Uses pattern detection and heuristics to break down complex tasks.
470
+
471
+ Args:
472
+ task_description: Task to decompose
473
+ max_subtasks: Maximum number of subtasks
474
+
475
+ Returns:
476
+ List of suggested subtasks with agent recommendations
477
+
478
+ Example:
479
+ >>> subtasks = sdk.strategic.suggest_task_decomposition(
480
+ ... "Implement OAuth2 authentication"
481
+ ... )
482
+ >>> for task in subtasks:
483
+ ... print(f"{task['task']} -> {task['agent']}")
484
+ """
485
+ try:
486
+ # Use orchestrator's decomposition method
487
+ orchestrator = self._sdk.orchestrator
488
+ result = orchestrator.suggest_task_decomposition(
489
+ task_description, max_subtasks
490
+ )
491
+ return list(result) if result else []
492
+ except Exception as e:
493
+ logger.error(f"Error suggesting task decomposition: {e}")
494
+ return []
495
+
496
+ def create_task_plan(
497
+ self,
498
+ task_description: str,
499
+ include_cost_estimate: bool = True,
500
+ ) -> dict[str, Any]:
501
+ """
502
+ Create a comprehensive task execution plan.
503
+
504
+ Combines pattern detection, cost optimization, and model selection.
505
+
506
+ Args:
507
+ task_description: Description of the task
508
+ include_cost_estimate: Whether to include cost estimates
509
+
510
+ Returns:
511
+ Comprehensive execution plan
512
+
513
+ Example:
514
+ >>> plan = sdk.strategic.create_task_plan(
515
+ ... "Add caching to API endpoints"
516
+ ... )
517
+ >>> print(f"Subtasks: {len(plan['subtasks'])}")
518
+ >>> print(f"Estimated tokens: {plan.get('total_estimated_tokens')}")
519
+ """
520
+ try:
521
+ orchestrator = self._sdk.orchestrator
522
+ result = orchestrator.create_task_suggestion(
523
+ task_description, include_cost_estimate
524
+ )
525
+ return (
526
+ dict(result) if result else {"task": task_description, "subtasks": []}
527
+ )
528
+ except Exception as e:
529
+ logger.error(f"Error creating task plan: {e}")
530
+ return {"task": task_description, "subtasks": []}
531
+
532
+
533
+ class StrategicAnalyticsMixin:
534
+ """
535
+ Mixin that adds strategic analytics capabilities to SDK.
536
+
537
+ Provides access to Phase 3 Strategic Analytics via sdk.strategic property.
538
+ """
539
+
540
+ _strategic: StrategicAnalyticsInterface | None = None
541
+
542
+ @property
543
+ def strategic(self) -> StrategicAnalyticsInterface:
544
+ """
545
+ Access strategic analytics interface.
546
+
547
+ Returns:
548
+ StrategicAnalyticsInterface for pattern detection, suggestions, etc.
549
+
550
+ Example:
551
+ >>> sdk = SDK(agent="claude")
552
+ >>> patterns = sdk.strategic.detect_patterns()
553
+ >>> suggestions = sdk.strategic.get_suggestions()
554
+ """
555
+ if self._strategic is None:
556
+ # Cast self to SDK for type checking
557
+ from typing import cast
558
+
559
+ from htmlgraph.sdk import SDK
560
+
561
+ sdk_self = cast(SDK, self)
562
+ self._strategic = StrategicAnalyticsInterface(_sdk=sdk_self)
563
+ return self._strategic
htmlgraph/server.py CHANGED
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  HtmlGraph REST API Server.
3
7
 
@@ -243,8 +247,8 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
243
247
  def do_GET(self) -> None:
244
248
  """Handle GET requests."""
245
249
  api, collection, node_id, params = self._parse_path()
246
- print(
247
- f"DEBUG do_GET: api={api}, collection={collection}, node_id={node_id}, params={params}"
250
+ logger.debug(
251
+ f"do_GET: api={api}, collection={collection}, node_id={node_id}, params={params}"
248
252
  )
249
253
 
250
254
  # Not an API request - serve static files
@@ -273,7 +277,7 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
273
277
 
274
278
  # GET /api/orchestration - Get delegation chains and agent coordination
275
279
  if collection == "orchestration":
276
- print(f"DEBUG: Handling orchestration request, params={params}")
280
+ logger.info(f"DEBUG: Handling orchestration request, params={params}")
277
281
  return self._handle_orchestration_view(params)
278
282
 
279
283
  # GET /api/task-delegations/stats - Get aggregated delegation statistics
@@ -1275,7 +1279,7 @@ class HtmlGraphAPIHandler(SimpleHTTPRequestHandler):
1275
1279
 
1276
1280
  def log_message(self, format: str, *args: str) -> None:
1277
1281
  """Custom log format."""
1278
- print(f"[{datetime.now().strftime('%H:%M:%S')}] {args[0]}")
1282
+ logger.info(f"[{datetime.now().strftime('%H:%M:%S')}] {args[0]}")
1279
1283
 
1280
1284
 
1281
1285
  def find_available_port(start_port: int = 8080, max_attempts: int = 10) -> int:
@@ -1434,7 +1438,7 @@ def serve(
1434
1438
  # Print warnings if any
1435
1439
  for warning in result.warnings:
1436
1440
  if not quiet:
1437
- print(f"⚠️ {warning}")
1441
+ logger.info(f"⚠️ {warning}")
1438
1442
 
1439
1443
  # Print server info
1440
1444
  if not quiet:
@@ -1473,31 +1477,31 @@ Press Ctrl+C to stop.
1473
1477
  asyncio.run(run_fastapi_server(result.handle))
1474
1478
 
1475
1479
  except PortInUseError:
1476
- print(f"\n❌ Port {port} is already in use\n")
1477
- print("Solutions:")
1478
- print(" 1. Use a different port:")
1479
- print(f" htmlgraph serve --port {port + 1}\n")
1480
- print(" 2. Let htmlgraph automatically find an available port:")
1481
- print(" htmlgraph serve --auto-port\n")
1482
- print(f" 3. Find and kill the process using port {port}:")
1483
- print(f" lsof -ti:{port} | xargs kill -9\n")
1480
+ logger.info(f"\n❌ Port {port} is already in use\n")
1481
+ logger.info("Solutions:")
1482
+ logger.info(" 1. Use a different port:")
1483
+ logger.info(f" htmlgraph serve --port {port + 1}\n")
1484
+ logger.info(" 2. Let htmlgraph automatically find an available port:")
1485
+ logger.info(" htmlgraph serve --auto-port\n")
1486
+ logger.info(f" 3. Find and kill the process using port {port}:")
1487
+ logger.info(f" lsof -ti:{port} | xargs kill -9\n")
1484
1488
 
1485
1489
  # Try to find and suggest an available port
1486
1490
  try:
1487
1491
  alt_port = find_available_port(port + 1)
1488
- print(f"💡 Found available port: {alt_port}")
1489
- print(f" Run: htmlgraph serve --port {alt_port}\n")
1492
+ logger.info(f"💡 Found available port: {alt_port}")
1493
+ logger.info(f" Run: htmlgraph serve --port {alt_port}\n")
1490
1494
  except OSError:
1491
1495
  pass
1492
1496
 
1493
1497
  sys.exit(1)
1494
1498
 
1495
1499
  except FastAPIServerError as e:
1496
- print(f"\n❌ Server error: {e}\n")
1500
+ logger.info(f"\n❌ Server error: {e}\n")
1497
1501
  sys.exit(1)
1498
1502
 
1499
1503
  except KeyboardInterrupt:
1500
- print("\nShutting down...")
1504
+ logger.info("\nShutting down...")
1501
1505
 
1502
1506
 
1503
1507
  if __name__ == "__main__":
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Session Warning System for AI Agents.
3
5
 
@@ -20,7 +22,6 @@ Usage:
20
22
  sdk.dismiss_session_warning()
21
23
  """
22
24
 
23
- from __future__ import annotations
24
25
 
25
26
  import json
26
27
  import sys
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Session Handoff and Continuity - Phase 2 Feature 3
3
5
 
@@ -19,11 +21,10 @@ Usage:
19
21
  # Resume next session
20
22
  resumed = sdk.sessions.continue_from_last()
21
23
  if resumed:
22
- print(resumed.summary)
23
- print(resumed.recommended_files)
24
+ logger.info("%s", resumed.summary)
25
+ logger.info("%s", resumed.recommended_files)
24
26
  """
25
27
 
26
- from __future__ import annotations
27
28
 
28
29
  import json
29
30
  import logging
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """System prompt management for HtmlGraph projects.
2
4
 
3
5
  Provides a two-tier system:
@@ -10,7 +12,6 @@ Architecture:
10
12
  - SDK provides methods for creation, validation, and management
11
13
  """
12
14
 
13
- from __future__ import annotations
14
15
 
15
16
  import logging
16
17
  from pathlib import Path