ds-agent-cli 0.1.0

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 (67) hide show
  1. package/bin/ds-agent.js +451 -0
  2. package/ds_agent/__init__.py +8 -0
  3. package/package.json +28 -0
  4. package/requirements.txt +126 -0
  5. package/setup.py +35 -0
  6. package/src/__init__.py +7 -0
  7. package/src/_compress_tool_result.py +118 -0
  8. package/src/api/__init__.py +4 -0
  9. package/src/api/app.py +1626 -0
  10. package/src/cache/__init__.py +5 -0
  11. package/src/cache/cache_manager.py +561 -0
  12. package/src/cli.py +2886 -0
  13. package/src/dynamic_prompts.py +281 -0
  14. package/src/orchestrator.py +4799 -0
  15. package/src/progress_manager.py +139 -0
  16. package/src/reasoning/__init__.py +332 -0
  17. package/src/reasoning/business_summary.py +431 -0
  18. package/src/reasoning/data_understanding.py +356 -0
  19. package/src/reasoning/model_explanation.py +383 -0
  20. package/src/reasoning/reasoning_trace.py +239 -0
  21. package/src/registry/__init__.py +3 -0
  22. package/src/registry/tools_registry.py +3 -0
  23. package/src/session_memory.py +448 -0
  24. package/src/session_store.py +370 -0
  25. package/src/storage/__init__.py +19 -0
  26. package/src/storage/artifact_store.py +620 -0
  27. package/src/storage/helpers.py +116 -0
  28. package/src/storage/huggingface_storage.py +694 -0
  29. package/src/storage/r2_storage.py +0 -0
  30. package/src/storage/user_files_service.py +288 -0
  31. package/src/tools/__init__.py +335 -0
  32. package/src/tools/advanced_analysis.py +823 -0
  33. package/src/tools/advanced_feature_engineering.py +708 -0
  34. package/src/tools/advanced_insights.py +578 -0
  35. package/src/tools/advanced_preprocessing.py +549 -0
  36. package/src/tools/advanced_training.py +906 -0
  37. package/src/tools/agent_tool_mapping.py +326 -0
  38. package/src/tools/auto_pipeline.py +420 -0
  39. package/src/tools/autogluon_training.py +1480 -0
  40. package/src/tools/business_intelligence.py +860 -0
  41. package/src/tools/cloud_data_sources.py +581 -0
  42. package/src/tools/code_interpreter.py +390 -0
  43. package/src/tools/computer_vision.py +614 -0
  44. package/src/tools/data_cleaning.py +614 -0
  45. package/src/tools/data_profiling.py +593 -0
  46. package/src/tools/data_type_conversion.py +268 -0
  47. package/src/tools/data_wrangling.py +433 -0
  48. package/src/tools/eda_reports.py +284 -0
  49. package/src/tools/enhanced_feature_engineering.py +241 -0
  50. package/src/tools/feature_engineering.py +302 -0
  51. package/src/tools/matplotlib_visualizations.py +1327 -0
  52. package/src/tools/model_training.py +520 -0
  53. package/src/tools/nlp_text_analytics.py +761 -0
  54. package/src/tools/plotly_visualizations.py +497 -0
  55. package/src/tools/production_mlops.py +852 -0
  56. package/src/tools/time_series.py +507 -0
  57. package/src/tools/tools_registry.py +2133 -0
  58. package/src/tools/visualization_engine.py +559 -0
  59. package/src/utils/__init__.py +42 -0
  60. package/src/utils/error_recovery.py +313 -0
  61. package/src/utils/parallel_executor.py +402 -0
  62. package/src/utils/polars_helpers.py +248 -0
  63. package/src/utils/schema_extraction.py +132 -0
  64. package/src/utils/semantic_layer.py +392 -0
  65. package/src/utils/token_budget.py +411 -0
  66. package/src/utils/validation.py +377 -0
  67. package/src/workflow_state.py +154 -0
@@ -0,0 +1,383 @@
1
+ """
2
+ Model Explanation Module
3
+
4
+ Provides reasoning about model behavior, performance, and interpretability.
5
+
6
+ KEY RULES:
7
+ - ✅ Accepts: Model metrics, predictions, feature importances
8
+ - ❌ NO: Raw model objects, training loops
9
+ - ✅ Returns: Explanations of WHY model behaves as it does
10
+ - ❌ NO: Model selection, hyperparameter choices
11
+
12
+ Use Cases:
13
+ 1. Explain model performance metrics
14
+ 2. Interpret feature importances
15
+ 3. Diagnose model failures
16
+ 4. Suggest model debugging steps
17
+
18
+ Example:
19
+ from reasoning.model_explanation import explain_model_performance
20
+
21
+ metrics = {
22
+ "accuracy": 0.95,
23
+ "precision": 0.92,
24
+ "recall": 0.88,
25
+ "confusion_matrix": [[800, 50], [100, 50]]
26
+ }
27
+
28
+ explanation = explain_model_performance(metrics, "classification")
29
+ # Returns: "Your model has high accuracy but low recall..."
30
+ """
31
+
32
+ from typing import Dict, Any, List, Optional
33
+ from . import get_reasoner
34
+
35
+
36
+ def explain_model_performance(
37
+ metrics: Dict[str, Any],
38
+ task_type: str,
39
+ baseline_metrics: Optional[Dict[str, Any]] = None
40
+ ) -> Dict[str, Any]:
41
+ """
42
+ Explain model performance metrics in plain English.
43
+
44
+ Args:
45
+ metrics: Performance metrics (accuracy, precision, recall, etc.)
46
+ task_type: 'classification' or 'regression'
47
+ baseline_metrics: Optional baseline to compare against
48
+
49
+ Returns:
50
+ {
51
+ "summary": str, # Overall assessment
52
+ "strengths": List[str], # What model does well
53
+ "weaknesses": List[str], # What model struggles with
54
+ "confusion_analysis": str, # Confusion matrix interpretation
55
+ "next_steps": List[str] # Suggested improvements
56
+ }
57
+ """
58
+ reasoner = get_reasoner()
59
+
60
+ comparison = ""
61
+ if baseline_metrics:
62
+ comparison = f"\n**Baseline Metrics (for comparison):**\n{baseline_metrics}"
63
+
64
+ prompt = f"""Analyze these model performance metrics:
65
+
66
+ **Task Type:** {task_type}
67
+
68
+ **Metrics:**
69
+ {metrics}{comparison}
70
+
71
+ Provide:
72
+ 1. Overall performance summary (good/bad/acceptable)
73
+ 2. Strengths (what model does well)
74
+ 3. Weaknesses (where model struggles)
75
+ 4. Confusion matrix analysis (if classification)
76
+ 5. Next steps for improvement
77
+
78
+ Be specific and actionable. If performance is poor, suggest why."""
79
+
80
+ system_prompt = """You are a model interpretation expert.
81
+ Explain performance metrics in terms business users understand.
82
+ Focus on actionable insights, not just numbers."""
83
+
84
+ schema = {
85
+ "summary": "string - Overall assessment",
86
+ "strengths": ["array of strengths"],
87
+ "weaknesses": ["array of weaknesses"],
88
+ "confusion_analysis": "string - Confusion matrix explanation",
89
+ "next_steps": ["array of improvement suggestions"]
90
+ }
91
+
92
+ return reasoner.reason_structured(prompt, schema, system_prompt)
93
+
94
+
95
+ def interpret_feature_importance(
96
+ feature_importances: Dict[str, float],
97
+ top_n: int = 10,
98
+ domain: Optional[str] = None
99
+ ) -> Dict[str, Any]:
100
+ """
101
+ Interpret feature importance scores and explain what they mean.
102
+
103
+ Args:
104
+ feature_importances: {feature_name: importance_score}
105
+ top_n: Number of top features to focus on
106
+ domain: Optional domain context
107
+
108
+ Returns:
109
+ {
110
+ "top_features": List[str], # Most important features
111
+ "interpretation": str, # What importances mean
112
+ "surprising_features": List[str], # Unexpectedly important/unimportant
113
+ "feature_relationships": str, # How features might interact
114
+ "recommendations": List[str] # What to investigate further
115
+ }
116
+ """
117
+ reasoner = get_reasoner()
118
+
119
+ # Sort by importance
120
+ sorted_features = sorted(
121
+ feature_importances.items(),
122
+ key=lambda x: x[1],
123
+ reverse=True
124
+ )[:top_n]
125
+
126
+ domain_context = f"\nDomain: {domain}" if domain else ""
127
+
128
+ prompt = f"""Interpret these feature importance scores:
129
+
130
+ **Top {top_n} Most Important Features:**
131
+ {dict(sorted_features)}
132
+
133
+ **All Features:**
134
+ {feature_importances}{domain_context}
135
+
136
+ Explain:
137
+ 1. What these importances tell us about the model
138
+ 2. Which features are surprisingly important/unimportant
139
+ 3. Potential feature interactions or relationships
140
+ 4. What to investigate further
141
+ 5. Whether importances make intuitive sense
142
+
143
+ Be specific about WHY certain features might be important."""
144
+
145
+ system_prompt = """You are a model interpretability expert.
146
+ Explain feature importances in domain terms, not just statistical terms.
147
+ Point out surprising or counterintuitive results."""
148
+
149
+ schema = {
150
+ "top_features": ["array of most important features"],
151
+ "interpretation": "string - What importances mean overall",
152
+ "surprising_features": ["array of unexpected results"],
153
+ "feature_relationships": "string - How features might interact",
154
+ "recommendations": ["array of investigation suggestions"]
155
+ }
156
+
157
+ return reasoner.reason_structured(prompt, schema, system_prompt)
158
+
159
+
160
+ def diagnose_model_failure(
161
+ failure_description: str,
162
+ model_type: str,
163
+ metrics: Dict[str, Any],
164
+ sample_predictions: Optional[List[Dict]] = None
165
+ ) -> Dict[str, Any]:
166
+ """
167
+ Diagnose why a model is failing and suggest fixes.
168
+
169
+ Args:
170
+ failure_description: Description of the problem
171
+ Example: "Model predicts all positives" or "Poor performance on test set"
172
+ model_type: Model algorithm used
173
+ metrics: Current performance metrics
174
+ sample_predictions: Optional sample of predictions vs actuals
175
+
176
+ Returns:
177
+ {
178
+ "diagnosis": str, # What's likely wrong
179
+ "root_causes": List[str], # Possible root causes
180
+ "debugging_steps": List[str], # How to investigate
181
+ "potential_fixes": List[str] # Suggested solutions
182
+ }
183
+ """
184
+ reasoner = get_reasoner()
185
+
186
+ samples = ""
187
+ if sample_predictions:
188
+ samples = f"\n**Sample Predictions:**\n{sample_predictions[:10]}"
189
+
190
+ prompt = f"""Diagnose this model failure:
191
+
192
+ **Problem:** {failure_description}
193
+
194
+ **Model Type:** {model_type}
195
+
196
+ **Current Metrics:**
197
+ {metrics}{samples}
198
+
199
+ Provide:
200
+ 1. Diagnosis of what's likely wrong
201
+ 2. Possible root causes
202
+ 3. Debugging steps to take
203
+ 4. Potential fixes to try
204
+
205
+ Be specific and prioritize most likely causes."""
206
+
207
+ system_prompt = """You are a model debugging expert.
208
+ Provide systematic diagnostic steps, not just guesses.
209
+ Prioritize most common failure modes first."""
210
+
211
+ schema = {
212
+ "diagnosis": "string - What's likely wrong",
213
+ "root_causes": ["array of possible causes"],
214
+ "debugging_steps": ["array of investigation steps"],
215
+ "potential_fixes": ["array of solutions to try"]
216
+ }
217
+
218
+ return reasoner.reason_structured(prompt, schema, system_prompt)
219
+
220
+
221
+ def explain_prediction(
222
+ prediction: Any,
223
+ feature_values: Dict[str, Any],
224
+ feature_contributions: Optional[Dict[str, float]] = None,
225
+ model_type: str = "unknown"
226
+ ) -> str:
227
+ """
228
+ Explain a single prediction in plain English.
229
+
230
+ Args:
231
+ prediction: Model's prediction
232
+ feature_values: Feature values for this prediction
233
+ feature_contributions: Optional SHAP values or contributions
234
+ model_type: Type of model
235
+
236
+ Returns:
237
+ Natural language explanation of the prediction
238
+ """
239
+ reasoner = get_reasoner()
240
+
241
+ contributions = ""
242
+ if feature_contributions:
243
+ contributions = f"\n**Feature Contributions:**\n{feature_contributions}"
244
+
245
+ prompt = f"""Explain this model prediction in simple terms:
246
+
247
+ **Prediction:** {prediction}
248
+
249
+ **Input Features:**
250
+ {feature_values}{contributions}
251
+
252
+ **Model Type:** {model_type}
253
+
254
+ Explain:
255
+ - What the model predicted
256
+ - Which features most influenced the prediction
257
+ - Why this prediction makes sense (or doesn't)
258
+ - How confident we should be in this prediction
259
+
260
+ Make it understandable to non-technical users."""
261
+
262
+ system_prompt = """You are explaining model predictions to business users.
263
+ Use plain English, avoid jargon, focus on the 'why' behind predictions."""
264
+
265
+ return reasoner.reason(prompt, system_prompt, temperature=0.1)
266
+
267
+
268
+ def compare_models(
269
+ model1_metrics: Dict[str, Any],
270
+ model2_metrics: Dict[str, Any],
271
+ model1_name: str = "Model A",
272
+ model2_name: str = "Model B",
273
+ business_context: Optional[str] = None
274
+ ) -> Dict[str, Any]:
275
+ """
276
+ Compare two models and recommend which to use.
277
+
278
+ Args:
279
+ model1_metrics: Metrics for first model
280
+ model2_metrics: Metrics for second model
281
+ model1_name: Name/description of first model
282
+ model2_name: Name/description of second model
283
+ business_context: Optional business requirements
284
+ Example: "Need high recall, false negatives are costly"
285
+
286
+ Returns:
287
+ {
288
+ "winner": str, # Which model is better
289
+ "comparison": str, # Detailed comparison
290
+ "tradeoffs": List[str], # Key tradeoffs
291
+ "recommendation": str, # Final recommendation
292
+ "context_considerations": str # Business context factors
293
+ }
294
+ """
295
+ reasoner = get_reasoner()
296
+
297
+ context = ""
298
+ if business_context:
299
+ context = f"\n**Business Context:**\n{business_context}"
300
+
301
+ prompt = f"""Compare these two models:
302
+
303
+ **{model1_name} Metrics:**
304
+ {model1_metrics}
305
+
306
+ **{model2_name} Metrics:**
307
+ {model2_metrics}{context}
308
+
309
+ Determine:
310
+ 1. Which model is objectively better (if any)
311
+ 2. Key differences and tradeoffs
312
+ 3. Which model to choose given business context
313
+ 4. When you might choose the "worse" model
314
+
315
+ Consider accuracy, precision, recall, training time, interpretability, etc."""
316
+
317
+ system_prompt = """You are a model selection expert.
318
+ Don't just pick the highest accuracy - consider tradeoffs and business needs.
319
+ Sometimes a simpler or faster model is better."""
320
+
321
+ schema = {
322
+ "winner": "string - Which model is better overall",
323
+ "comparison": "string - Detailed comparison",
324
+ "tradeoffs": ["array of key tradeoffs"],
325
+ "recommendation": "string - Final recommendation with reasoning",
326
+ "context_considerations": "string - How business context affects choice"
327
+ }
328
+
329
+ return reasoner.reason_structured(prompt, schema, system_prompt)
330
+
331
+
332
+ def explain_overfitting(
333
+ train_metrics: Dict[str, float],
334
+ test_metrics: Dict[str, float],
335
+ model_complexity: Optional[str] = None
336
+ ) -> Dict[str, Any]:
337
+ """
338
+ Detect and explain overfitting (or underfitting).
339
+
340
+ Args:
341
+ train_metrics: Training set metrics
342
+ test_metrics: Test set metrics
343
+ model_complexity: Optional description of model complexity
344
+
345
+ Returns:
346
+ {
347
+ "diagnosis": str, # Overfitting/underfitting/good_fit
348
+ "severity": str, # Low/medium/high
349
+ "explanation": str, # Why this is happening
350
+ "solutions": List[str] # How to fix it
351
+ }
352
+ """
353
+ reasoner = get_reasoner()
354
+
355
+ prompt = f"""Analyze these train vs test metrics for overfitting:
356
+
357
+ **Training Metrics:**
358
+ {train_metrics}
359
+
360
+ **Test Metrics:**
361
+ {test_metrics}
362
+
363
+ **Model Complexity:** {model_complexity or 'Unknown'}
364
+
365
+ Determine:
366
+ 1. Whether model is overfitting, underfitting, or well-fitted
367
+ 2. Severity of the problem
368
+ 3. Why this is happening
369
+ 4. Specific solutions to try
370
+
371
+ Be specific about the gap between train and test performance."""
372
+
373
+ system_prompt = """You are a model diagnostics expert.
374
+ Explain overfitting in practical terms and provide actionable solutions."""
375
+
376
+ schema = {
377
+ "diagnosis": "string - overfitting/underfitting/good_fit",
378
+ "severity": "string - low/medium/high",
379
+ "explanation": "string - Why this is happening",
380
+ "solutions": ["array of specific fixes to try"]
381
+ }
382
+
383
+ return reasoner.reason_structured(prompt, schema, system_prompt)
@@ -0,0 +1,239 @@
1
+ """
2
+ Reasoning Trace Module
3
+
4
+ Captures decision-making process for transparency and debugging.
5
+ Provides audit trail of why certain tools/agents were chosen.
6
+ """
7
+
8
+ from typing import Dict, Any, List, Optional
9
+ from datetime import datetime
10
+ import json
11
+
12
+
13
+ class ReasoningTrace:
14
+ """
15
+ Records reasoning decisions made during workflow execution.
16
+
17
+ Provides transparency into:
18
+ - Why specific agents were selected
19
+ - Why certain tools were chosen
20
+ - What alternatives were considered
21
+ - Decision confidence levels
22
+ """
23
+
24
+ def __init__(self):
25
+ self.trace_history: List[Dict[str, Any]] = []
26
+ self.current_context = {}
27
+
28
+ def record_agent_selection(self, task: str, selected_agent: str,
29
+ confidence: float, alternatives: Dict[str, float] = None):
30
+ """
31
+ Record why a specific agent was selected.
32
+
33
+ Args:
34
+ task: User's task description
35
+ selected_agent: Agent that was selected
36
+ confidence: Confidence score (0-1)
37
+ alternatives: Other agents considered with their scores
38
+ """
39
+ decision = {
40
+ "timestamp": datetime.now().isoformat(),
41
+ "type": "agent_selection",
42
+ "task": task,
43
+ "decision": selected_agent,
44
+ "confidence": confidence,
45
+ "alternatives": alternatives or {},
46
+ "reasoning": self._explain_agent_selection(task, selected_agent, confidence)
47
+ }
48
+
49
+ self.trace_history.append(decision)
50
+ print(f"📝 Reasoning: Selected {selected_agent} (confidence: {confidence:.2f})")
51
+
52
+ def record_tool_selection(self, tool_name: str, args: Dict[str, Any],
53
+ reason: str, iteration: int):
54
+ """
55
+ Record why a specific tool was chosen.
56
+
57
+ Args:
58
+ tool_name: Tool that was selected
59
+ args: Arguments passed to tool
60
+ reason: Human-readable reason for selection
61
+ iteration: Current workflow iteration
62
+ """
63
+ decision = {
64
+ "timestamp": datetime.now().isoformat(),
65
+ "type": "tool_selection",
66
+ "iteration": iteration,
67
+ "tool": tool_name,
68
+ "arguments": self._sanitize_args(args),
69
+ "reason": reason
70
+ }
71
+
72
+ self.trace_history.append(decision)
73
+
74
+ def record_agent_handoff(self, from_agent: str, to_agent: str,
75
+ reason: str, iteration: int):
76
+ """
77
+ Record agent hand-off decision.
78
+
79
+ Args:
80
+ from_agent: Previous agent
81
+ to_agent: New agent
82
+ reason: Why hand-off occurred
83
+ iteration: Current workflow iteration
84
+ """
85
+ decision = {
86
+ "timestamp": datetime.now().isoformat(),
87
+ "type": "agent_handoff",
88
+ "iteration": iteration,
89
+ "from": from_agent,
90
+ "to": to_agent,
91
+ "reason": reason
92
+ }
93
+
94
+ self.trace_history.append(decision)
95
+ print(f"📝 Reasoning: Hand-off {from_agent} → {to_agent} - {reason}")
96
+
97
+ def record_decision_point(self, decision_type: str, options: List[str],
98
+ chosen: str, reason: str):
99
+ """
100
+ Record a general decision point.
101
+
102
+ Args:
103
+ decision_type: Type of decision (e.g., "feature_selection", "model_type")
104
+ options: Options that were available
105
+ chosen: Option that was selected
106
+ reason: Why this option was chosen
107
+ """
108
+ decision = {
109
+ "timestamp": datetime.now().isoformat(),
110
+ "type": decision_type,
111
+ "options": options,
112
+ "chosen": chosen,
113
+ "reason": reason
114
+ }
115
+
116
+ self.trace_history.append(decision)
117
+
118
+ def get_trace(self) -> List[Dict[str, Any]]:
119
+ """Get full reasoning trace."""
120
+ return self.trace_history
121
+
122
+ def get_trace_summary(self) -> str:
123
+ """
124
+ Get human-readable summary of reasoning trace.
125
+
126
+ Returns:
127
+ Formatted string summarizing all decisions
128
+ """
129
+ if not self.trace_history:
130
+ return "No reasoning trace available."
131
+
132
+ summary_parts = ["## Reasoning Trace\n"]
133
+
134
+ for i, decision in enumerate(self.trace_history, 1):
135
+ decision_type = decision.get("type", "unknown")
136
+ timestamp = decision.get("timestamp", "")
137
+
138
+ if decision_type == "agent_selection":
139
+ summary_parts.append(
140
+ f"{i}. **Agent Selection** ({timestamp})\n"
141
+ f" - Selected: {decision.get('decision')}\n"
142
+ f" - Confidence: {decision.get('confidence', 0):.2f}\n"
143
+ f" - Reasoning: {decision.get('reasoning', 'N/A')}\n"
144
+ )
145
+
146
+ elif decision_type == "tool_selection":
147
+ summary_parts.append(
148
+ f"{i}. **Tool Execution** (Iteration {decision.get('iteration')})\n"
149
+ f" - Tool: {decision.get('tool')}\n"
150
+ f" - Reason: {decision.get('reason', 'N/A')}\n"
151
+ )
152
+
153
+ elif decision_type == "agent_handoff":
154
+ summary_parts.append(
155
+ f"{i}. **Agent Hand-off** (Iteration {decision.get('iteration')})\n"
156
+ f" - From: {decision.get('from')}\n"
157
+ f" - To: {decision.get('to')}\n"
158
+ f" - Reason: {decision.get('reason', 'N/A')}\n"
159
+ )
160
+
161
+ else:
162
+ summary_parts.append(
163
+ f"{i}. **{decision_type}** ({timestamp})\n"
164
+ f" - Chosen: {decision.get('chosen', 'N/A')}\n"
165
+ f" - Reason: {decision.get('reason', 'N/A')}\n"
166
+ )
167
+
168
+ return "\n".join(summary_parts)
169
+
170
+ def export_trace(self, file_path: str = "reasoning_trace.json"):
171
+ """
172
+ Export reasoning trace to JSON file.
173
+
174
+ Args:
175
+ file_path: Path to save trace file
176
+ """
177
+ with open(file_path, 'w') as f:
178
+ json.dump(self.trace_history, f, indent=2)
179
+
180
+ print(f"📄 Reasoning trace exported to {file_path}")
181
+
182
+ def _explain_agent_selection(self, task: str, agent: str, confidence: float) -> str:
183
+ """Generate explanation for agent selection."""
184
+ if confidence > 0.9:
185
+ certainty = "High confidence"
186
+ elif confidence > 0.7:
187
+ certainty = "Moderate confidence"
188
+ else:
189
+ certainty = "Low confidence"
190
+
191
+ agent_explanations = {
192
+ "data_quality_agent": "Task involves data profiling, quality assessment, or initial exploration",
193
+ "preprocessing_agent": "Task requires data cleaning, transformation, or feature engineering",
194
+ "visualization_agent": "Task focuses on creating visualizations, charts, or dashboards",
195
+ "modeling_agent": "Task involves machine learning model training or evaluation",
196
+ "time_series_agent": "Task involves time series analysis, forecasting, or temporal patterns",
197
+ "nlp_agent": "Task involves text processing, sentiment analysis, or NLP operations",
198
+ "business_intelligence_agent": "Task requires business metrics, KPIs, or strategic insights",
199
+ "production_agent": "Task involves model deployment, monitoring, or production operations"
200
+ }
201
+
202
+ explanation = agent_explanations.get(
203
+ agent,
204
+ "Selected based on task keywords and context"
205
+ )
206
+
207
+ return f"{certainty}: {explanation}"
208
+
209
+ def _sanitize_args(self, args: Dict[str, Any]) -> Dict[str, Any]:
210
+ """Remove sensitive data from arguments before logging."""
211
+ sanitized = {}
212
+
213
+ for key, value in args.items():
214
+ if key in ["api_key", "password", "token", "secret"]:
215
+ sanitized[key] = "***REDACTED***"
216
+ elif isinstance(value, str) and len(value) > 100:
217
+ sanitized[key] = value[:97] + "..."
218
+ else:
219
+ sanitized[key] = value
220
+
221
+ return sanitized
222
+
223
+
224
+ # Global reasoning trace instance
225
+ _reasoning_trace = None
226
+
227
+
228
+ def get_reasoning_trace() -> ReasoningTrace:
229
+ """Get or create global reasoning trace instance."""
230
+ global _reasoning_trace
231
+ if _reasoning_trace is None:
232
+ _reasoning_trace = ReasoningTrace()
233
+ return _reasoning_trace
234
+
235
+
236
+ def reset_reasoning_trace():
237
+ """Reset reasoning trace for new workflow."""
238
+ global _reasoning_trace
239
+ _reasoning_trace = ReasoningTrace()
@@ -0,0 +1,3 @@
1
+ from .tools_registry import TOOLS
2
+
3
+ __all__ = ["TOOLS"]
@@ -0,0 +1,3 @@
1
+ from ds_agent.tools.tools_registry import TOOLS
2
+
3
+ __all__ = ["TOOLS"]