deepset-mcp 0.0.3rc1__py3-none-any.whl → 0.0.4__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 (115) hide show
  1. deepset_mcp/__init__.py +10 -0
  2. deepset_mcp/api/__init__.py +4 -0
  3. deepset_mcp/api/client.py +4 -0
  4. deepset_mcp/api/custom_components/__init__.py +4 -0
  5. deepset_mcp/api/custom_components/models.py +4 -0
  6. deepset_mcp/api/custom_components/protocols.py +4 -0
  7. deepset_mcp/api/custom_components/resource.py +4 -0
  8. deepset_mcp/api/exceptions.py +4 -0
  9. deepset_mcp/api/haystack_service/__init__.py +4 -0
  10. deepset_mcp/api/haystack_service/protocols.py +4 -0
  11. deepset_mcp/api/haystack_service/resource.py +4 -0
  12. deepset_mcp/api/indexes/__init__.py +4 -0
  13. deepset_mcp/api/indexes/models.py +4 -0
  14. deepset_mcp/api/indexes/protocols.py +4 -0
  15. deepset_mcp/api/indexes/resource.py +4 -0
  16. deepset_mcp/api/integrations/__init__.py +4 -0
  17. deepset_mcp/api/integrations/models.py +4 -0
  18. deepset_mcp/api/integrations/protocols.py +4 -0
  19. deepset_mcp/api/integrations/resource.py +4 -0
  20. deepset_mcp/api/pipeline/__init__.py +4 -0
  21. deepset_mcp/api/pipeline/log_level.py +4 -0
  22. deepset_mcp/api/pipeline/models.py +4 -0
  23. deepset_mcp/api/pipeline/protocols.py +8 -0
  24. deepset_mcp/api/pipeline/resource.py +4 -0
  25. deepset_mcp/api/pipeline_template/__init__.py +4 -0
  26. deepset_mcp/api/pipeline_template/models.py +4 -0
  27. deepset_mcp/api/pipeline_template/protocols.py +4 -0
  28. deepset_mcp/api/pipeline_template/resource.py +4 -0
  29. deepset_mcp/api/protocols.py +4 -0
  30. deepset_mcp/api/secrets/__init__.py +4 -0
  31. deepset_mcp/api/secrets/models.py +4 -0
  32. deepset_mcp/api/secrets/protocols.py +4 -0
  33. deepset_mcp/api/secrets/resource.py +4 -0
  34. deepset_mcp/api/shared_models.py +4 -0
  35. deepset_mcp/api/transport.py +4 -0
  36. deepset_mcp/api/user/__init__.py +4 -0
  37. deepset_mcp/api/user/protocols.py +4 -0
  38. deepset_mcp/api/user/resource.py +4 -0
  39. deepset_mcp/api/workspace/__init__.py +4 -0
  40. deepset_mcp/api/workspace/models.py +4 -0
  41. deepset_mcp/api/workspace/protocols.py +4 -0
  42. deepset_mcp/api/workspace/resource.py +4 -0
  43. deepset_mcp/config.py +8 -0
  44. deepset_mcp/initialize_embedding_model.py +4 -0
  45. deepset_mcp/main.py +8 -0
  46. deepset_mcp/store.py +4 -0
  47. deepset_mcp/tool_factory.py +11 -4
  48. deepset_mcp/tools/__init__.py +4 -0
  49. deepset_mcp/tools/custom_components.py +4 -0
  50. deepset_mcp/tools/doc_search.py +4 -0
  51. deepset_mcp/tools/haystack_service.py +4 -0
  52. deepset_mcp/tools/haystack_service_models.py +4 -0
  53. deepset_mcp/tools/indexes.py +4 -0
  54. deepset_mcp/tools/model_protocol.py +4 -0
  55. deepset_mcp/tools/pipeline.py +4 -0
  56. deepset_mcp/tools/pipeline_template.py +4 -0
  57. deepset_mcp/tools/secrets.py +4 -0
  58. deepset_mcp/tools/tokonomics/__init__.py +4 -0
  59. deepset_mcp/tools/tokonomics/decorators.py +4 -0
  60. deepset_mcp/tools/tokonomics/explorer.py +4 -0
  61. deepset_mcp/tools/tokonomics/object_store.py +4 -0
  62. deepset_mcp/tools/workspace.py +4 -0
  63. deepset_mcp-0.0.4.dist-info/METADATA +761 -0
  64. deepset_mcp-0.0.4.dist-info/RECORD +70 -0
  65. {deepset_mcp-0.0.3rc1.dist-info → deepset_mcp-0.0.4.dist-info}/entry_points.txt +0 -1
  66. deepset_mcp-0.0.4.dist-info/licenses/LICENSE +202 -0
  67. deepset_mcp/agents/__init__.py +0 -0
  68. deepset_mcp/agents/debugging/__init__.py +0 -0
  69. deepset_mcp/agents/debugging/debugging_agent.py +0 -37
  70. deepset_mcp/agents/debugging/system_prompt.md +0 -214
  71. deepset_mcp/agents/generalist/__init__.py +0 -0
  72. deepset_mcp/agents/generalist/generalist_agent.py +0 -38
  73. deepset_mcp/agents/generalist/system_prompt.md +0 -241
  74. deepset_mcp/benchmark/README.md +0 -425
  75. deepset_mcp/benchmark/__init__.py +0 -1
  76. deepset_mcp/benchmark/agent_configs/debugging_agent.yml +0 -10
  77. deepset_mcp/benchmark/agent_configs/generalist_agent.yml +0 -6
  78. deepset_mcp/benchmark/dp_validation_error_analysis/__init__.py +0 -0
  79. deepset_mcp/benchmark/dp_validation_error_analysis/eda.ipynb +0 -757
  80. deepset_mcp/benchmark/dp_validation_error_analysis/prepare_interaction_data.ipynb +0 -167
  81. deepset_mcp/benchmark/dp_validation_error_analysis/preprocessing_utils.py +0 -213
  82. deepset_mcp/benchmark/runner/__init__.py +0 -0
  83. deepset_mcp/benchmark/runner/agent_benchmark_runner.py +0 -561
  84. deepset_mcp/benchmark/runner/agent_loader.py +0 -110
  85. deepset_mcp/benchmark/runner/cli.py +0 -39
  86. deepset_mcp/benchmark/runner/cli_agent.py +0 -373
  87. deepset_mcp/benchmark/runner/cli_index.py +0 -71
  88. deepset_mcp/benchmark/runner/cli_pipeline.py +0 -73
  89. deepset_mcp/benchmark/runner/cli_tests.py +0 -226
  90. deepset_mcp/benchmark/runner/cli_utils.py +0 -61
  91. deepset_mcp/benchmark/runner/config.py +0 -73
  92. deepset_mcp/benchmark/runner/config_loader.py +0 -64
  93. deepset_mcp/benchmark/runner/interactive.py +0 -140
  94. deepset_mcp/benchmark/runner/models.py +0 -203
  95. deepset_mcp/benchmark/runner/repl.py +0 -67
  96. deepset_mcp/benchmark/runner/setup_actions.py +0 -238
  97. deepset_mcp/benchmark/runner/streaming.py +0 -360
  98. deepset_mcp/benchmark/runner/teardown_actions.py +0 -196
  99. deepset_mcp/benchmark/runner/tracing.py +0 -21
  100. deepset_mcp/benchmark/tasks/chat_rag_answers_wrong_format.yml +0 -16
  101. deepset_mcp/benchmark/tasks/documents_output_wrong.yml +0 -13
  102. deepset_mcp/benchmark/tasks/jinja_str_instead_of_complex_type.yml +0 -11
  103. deepset_mcp/benchmark/tasks/jinja_syntax_error.yml +0 -11
  104. deepset_mcp/benchmark/tasks/missing_output_mapping.yml +0 -14
  105. deepset_mcp/benchmark/tasks/no_query_input.yml +0 -13
  106. deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_str.yml +0 -141
  107. deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_syntax.yml +0 -141
  108. deepset_mcp/benchmark/tasks/pipelines/chat_rag_answers_wrong_format.yml +0 -181
  109. deepset_mcp/benchmark/tasks/pipelines/chat_rag_missing_output_mapping.yml +0 -189
  110. deepset_mcp/benchmark/tasks/pipelines/rag_documents_wrong_format.yml +0 -193
  111. deepset_mcp/benchmark/tasks/pipelines/rag_no_query_input.yml +0 -191
  112. deepset_mcp/benchmark/tasks/pipelines/standard_index.yml +0 -167
  113. deepset_mcp-0.0.3rc1.dist-info/METADATA +0 -289
  114. deepset_mcp-0.0.3rc1.dist-info/RECORD +0 -115
  115. {deepset_mcp-0.0.3rc1.dist-info → deepset_mcp-0.0.4.dist-info}/WHEEL +0 -0
@@ -1,360 +0,0 @@
1
- """
2
- Async-compatible practical streaming callback for deepset agent responses.
3
-
4
- Handles text streaming, tool calls, and tool results with nice console formatting.
5
- """
6
-
7
- import json
8
- from typing import Any
9
-
10
- from haystack.dataclasses.streaming_chunk import StreamingChunk
11
- from rich.console import Console
12
- from rich.live import Live
13
- from rich.markdown import Markdown
14
-
15
-
16
- class StreamingCallbackManager:
17
- """
18
- Async-compatible callback tailored to your exact streaming structure.
19
-
20
- Handles the specific patterns from your deepset agent.
21
- """
22
-
23
- def __init__(self) -> None:
24
- """Initialize the streaming callback."""
25
- self.console = Console()
26
- self.active_tools: dict[int, dict[str, Any]] = {}
27
- self.accumulated_text = ""
28
- self.live_display: Live | None = None
29
- self.text_started = False
30
-
31
- async def __call__(self, chunk: StreamingChunk) -> None:
32
- """Process each streaming chunk asynchronously."""
33
- await self._handle_chunk(chunk)
34
-
35
- async def _handle_chunk(self, chunk: StreamingChunk) -> None:
36
- """Handle different types of chunks based on your data structure."""
37
- meta = chunk.meta
38
-
39
- # 1. Handle text streaming (like "I'll help you troubleshoot...")
40
- if self._is_text_delta(meta):
41
- text = meta["delta"]["text"]
42
- self.accumulated_text += text
43
- await self._render_markdown_optimistic()
44
-
45
- # 2. Handle tool call start (like list_pipelines, get_pipeline)
46
- elif self._is_tool_start(meta):
47
- await self._handle_tool_start(meta)
48
-
49
- # 3. Handle tool arguments streaming (partial JSON)
50
- elif self._is_tool_args(meta):
51
- await self._handle_tool_args(meta)
52
-
53
- # 4. Handle tool results
54
- elif self._is_tool_result(meta):
55
- await self._handle_tool_result(meta)
56
-
57
- # 5. Handle message deltas (usage info, etc.)
58
- elif self._is_message_delta(meta):
59
- await self._handle_message_delta(meta)
60
-
61
- if self._is_finish_event(meta):
62
- await self._handle_finish_event(meta)
63
-
64
- async def _render_markdown_optimistic(self) -> None:
65
- """Render accumulated text as markdown optimistically."""
66
- if not self.accumulated_text.strip():
67
- return
68
-
69
- try:
70
- # Attempt to render as markdown
71
- markdown = Markdown(self.accumulated_text)
72
-
73
- # Start live display if not already started
74
- if not self.live_display:
75
- self.live_display = Live(markdown, console=self.console, refresh_per_second=10)
76
- self.live_display.start()
77
- self.text_started = True
78
- else:
79
- # Update the live display
80
- self.live_display.update(markdown)
81
-
82
- except Exception:
83
- # Fallback to plain text if markdown parsing fails
84
- if not self.live_display:
85
- self.live_display = Live(self.accumulated_text, console=self.console, refresh_per_second=10)
86
- self.live_display.start()
87
- self.text_started = True
88
- else:
89
- self.live_display.update(self.accumulated_text)
90
-
91
- def _is_text_delta(self, meta: dict[str, Any]) -> bool:
92
- """Check if this is a text streaming chunk."""
93
- return meta.get("type") == "content_block_delta" and meta.get("delta", {}).get("type") == "text_delta"
94
-
95
- def _is_tool_start(self, meta: dict[str, Any]) -> bool:
96
- """Check if this is the start of a tool call."""
97
- return meta.get("type") == "content_block_start" and meta.get("content_block", {}).get("type") == "tool_use"
98
-
99
- def _is_tool_args(self, meta: dict[str, Any]) -> bool:
100
- """Check if this is tool arguments streaming."""
101
- return meta.get("type") == "content_block_delta" and meta.get("delta", {}).get("type") == "input_json_delta"
102
-
103
- def _is_tool_result(self, meta: dict[str, Any]) -> bool:
104
- """Check if this is a tool result."""
105
- return "tool_result" in meta and "tool_call" in meta
106
-
107
- def _is_message_delta(self, meta: dict[str, Any]) -> bool:
108
- """Check if this is a message-level delta."""
109
- return meta.get("type") == "message_delta"
110
-
111
- def _is_finish_event(self, meta: dict[str, Any]) -> bool:
112
- """Check if this is a finish event."""
113
- return "stop_reason" in meta.get("delta", {})
114
-
115
- async def _handle_tool_start(self, meta: dict[str, Any]) -> None:
116
- """Handle the start of a tool call."""
117
- content_block = meta["content_block"]
118
- tool_name = content_block["name"]
119
- tool_id = content_block["id"]
120
- index = meta["index"]
121
-
122
- # Stop live display if active
123
- if self.live_display:
124
- self.live_display.stop()
125
- self.live_display = None
126
-
127
- # Store tool state
128
- self.active_tools[index] = {
129
- "name": tool_name,
130
- "id": tool_id,
131
- "args_json": "",
132
- "started": True,
133
- "args_displayed": False,
134
- }
135
-
136
- # Display tool call header (text accumulation continues after tools)
137
- self.console.print() # New line
138
- self.console.print("┌─ 🔧 Tool Call", style="bold cyan")
139
- self.console.print(f"│ Name: {tool_name}", style="cyan")
140
-
141
- async def _handle_tool_args(self, meta: dict[str, Any]) -> None:
142
- """Handle streaming tool arguments."""
143
- index = meta["index"]
144
- if index not in self.active_tools:
145
- return
146
-
147
- partial_json = meta["delta"]["partial_json"]
148
- self.active_tools[index]["args_json"] += partial_json
149
-
150
- # Try to show current args when we have complete JSON
151
- await self._try_display_complete_args(index)
152
-
153
- async def _try_display_complete_args(self, index: int) -> None:
154
- """Try to display complete arguments when JSON is valid."""
155
- tool = self.active_tools[index]
156
-
157
- try:
158
- # Try to parse the current JSON
159
- if tool["args_json"].strip() and not tool["args_displayed"]:
160
- args = json.loads(tool["args_json"])
161
-
162
- # Display arguments in multi-line format
163
- await self._display_tool_arguments(args)
164
- tool["args_displayed"] = True
165
-
166
- except json.JSONDecodeError:
167
- # Still accumulating JSON, wait for more
168
- pass
169
-
170
- async def _display_tool_arguments(self, args: dict[str, Any]) -> None:
171
- """Display tool arguments in a pretty multi-line format."""
172
- if not args:
173
- self.console.print("│ (no arguments)", style="dim")
174
- return
175
-
176
- self.console.print("│ Arguments:", style="cyan")
177
-
178
- for arg_name, arg_value in args.items():
179
- self.console.print(f"│ {arg_name}:", style="yellow")
180
-
181
- # Format the argument value with line limit
182
- formatted_value = await self._format_argument_value(arg_value, max_lines=5)
183
-
184
- # Display each line of the value with proper indentation
185
- for line in formatted_value:
186
- self.console.print(f"│ {line}", style="white")
187
-
188
- async def _format_argument_value(self, value: Any, max_lines: int = 5) -> list[str]:
189
- """Format an argument value with line limits."""
190
- if value is None:
191
- return ["null"]
192
-
193
- if isinstance(value, bool):
194
- return [str(value).lower()]
195
-
196
- if isinstance(value, int | float):
197
- return [str(value)]
198
-
199
- if isinstance(value, str):
200
- # Handle multi-line strings
201
- lines = value.split("\n")
202
-
203
- # Limit lines
204
- display_lines = lines[:max_lines]
205
- result = []
206
-
207
- for line in display_lines:
208
- # Wrap long lines at 60 characters for readability
209
- if len(line) <= 60:
210
- result.append(f'"{line}"' if line else '""')
211
- else:
212
- result.append(f'"{line[:57]}..."')
213
-
214
- # Add truncation indicator if needed
215
- if len(lines) > max_lines:
216
- result.append(f"... ({len(lines) - max_lines} more lines)")
217
-
218
- return result
219
-
220
- if isinstance(value, list | dict):
221
- # Pretty print complex objects
222
- try:
223
- json_str = json.dumps(value, indent=2)
224
- lines = json_str.split("\n")
225
-
226
- display_lines = lines[:max_lines]
227
- if len(lines) > max_lines:
228
- display_lines.append(f"... ({len(lines) - max_lines} more lines)")
229
-
230
- return display_lines
231
- except Exception:
232
- return [str(value)[:100] + "..." if len(str(value)) > 100 else str(value)]
233
-
234
- # Fallback for other types
235
- str_value = str(value)
236
- if len(str_value) > 60:
237
- return [str_value[:57] + "..."]
238
- return [str_value]
239
-
240
- async def _handle_tool_result(self, meta: dict[str, Any]) -> None:
241
- """Handle tool execution results."""
242
- tool_result = meta["tool_result"]
243
-
244
- # Close the tool call display
245
- self.console.print("└─ ✅ Completed", style="green")
246
-
247
- # Display tool result content (max 10 lines)
248
- if tool_result:
249
- await self._display_tool_result(tool_result)
250
-
251
- async def _display_tool_result(self, tool_result: str, max_lines: int = 10) -> None:
252
- """Display tool result with a maximum number of lines."""
253
- try:
254
- # Parse the tool result JSON
255
- if isinstance(tool_result, str):
256
- result_data = json.loads(tool_result)
257
-
258
- # Extract the actual content
259
- content_text = await self._extract_result_content(result_data)
260
-
261
- if content_text:
262
- # Split into lines and limit to max_lines
263
- lines = content_text.split("\n")
264
- display_lines = lines[:max_lines]
265
-
266
- # Show the result with indentation
267
- self.console.print(" ┌─ Result:", style="dim cyan")
268
- for line in display_lines:
269
- if line.strip(): # Only show non-empty lines
270
- self.console.print(f" │ {line}", style="dim")
271
-
272
- # Show truncation indicator if needed
273
- if len(lines) > max_lines:
274
- remaining = len(lines) - max_lines
275
- self.console.print(f" │ ... ({remaining} more lines)", style="dim yellow")
276
-
277
- self.console.print(" └─", style="dim cyan")
278
- else:
279
- self.console.print(" → Result received", style="dim green")
280
-
281
- except Exception:
282
- # Fallback for unparseable results
283
- self.console.print(" → Result received", style="dim green")
284
-
285
- async def _extract_result_content(self, result_data: dict[str, Any]) -> str | None:
286
- """Extract meaningful content from tool result data."""
287
- try:
288
- # Handle the specific structure from your deepset results
289
- if isinstance(result_data, dict):
290
- content = result_data.get("content", [])
291
-
292
- if isinstance(content, list) and content:
293
- # Get the first content item
294
- first_content = content[0]
295
-
296
- if isinstance(first_content, dict):
297
- text_content = first_content.get("text", "")
298
-
299
- # Handle nested JSON strings (like "@obj_001 → deepset_mcp...")
300
- if text_content.startswith('"') and text_content.endswith('"'):
301
- # Parse the inner JSON string
302
- inner_content = json.loads(text_content)
303
- formatted = await self._format_deepset_content(str(inner_content))
304
- return formatted
305
- else:
306
- return str(text_content) if text_content else None
307
-
308
- return str(result_data) if result_data else None
309
-
310
- except Exception:
311
- return str(result_data) if result_data else None
312
-
313
- async def _format_deepset_content(self, content: str) -> str:
314
- """Format deepset-specific content for better readability."""
315
- try:
316
- # Handle content like "@obj_001 → deepset_mcp.api.pipeline.models.PipelineList..."
317
- if " → " in content:
318
- parts = content.split(" → ", 1)
319
- if len(parts) == 2:
320
- obj_id, obj_content = parts
321
-
322
- # Clean up the object content for better display
323
- formatted = obj_content.replace("\\n", "\n").replace("\\\\", "\\")
324
-
325
- # Add object ID as header
326
- return f"Object: {obj_id}\n{formatted}"
327
-
328
- # Fallback: clean up escape sequences
329
- return content.replace("\\n", "\n").replace("\\\\", "\\")
330
-
331
- except Exception:
332
- return content
333
-
334
- async def _handle_message_delta(self, meta: dict[str, Any]) -> None:
335
- """Handle message-level information."""
336
- delta = meta.get("delta", {})
337
-
338
- # Could show usage info if desired
339
- if "usage" in delta:
340
- usage = delta["usage"]
341
- if usage.get("output_tokens"):
342
- # Optionally show token usage
343
- pass
344
-
345
- async def _handle_finish_event(self, meta: dict[str, Any]) -> None:
346
- """Handle finish events."""
347
- finish_reason = meta.get("delta", {}).get("stop_reason")
348
- if finish_reason == "tool_call_results":
349
- # Clean up after tool calls
350
- self.active_tools.clear()
351
- self.console.print() # Extra line after tools
352
- elif finish_reason == "end_turn":
353
- # Stop live display and reset for next interaction
354
- if self.live_display:
355
- self.live_display.stop()
356
- self.live_display = None
357
- # Ensure cursor is on a new line for the next prompt
358
- self.console.print()
359
- self.accumulated_text = ""
360
- self.text_started = False
@@ -1,196 +0,0 @@
1
- import asyncio
2
- import os
3
- from typing import Any
4
-
5
- from deepset_mcp.api.client import AsyncDeepsetClient
6
- from deepset_mcp.benchmark.runner.models import TestCaseConfig
7
-
8
-
9
- def _get_api_key(explicit_key: str | None) -> str:
10
- """
11
- Return whichever API key to use: explicit_key takes precedence, otherwise read DP_API_KEY from the environment.
12
-
13
- If still missing, raise ValueError.
14
- """
15
- if explicit_key:
16
- return explicit_key
17
- env_key = os.getenv("DP_API_KEY")
18
- if not env_key:
19
- raise ValueError("No API key provided: pass --api-key or set DP_API_KEY in env.")
20
- return env_key
21
-
22
-
23
- # ─────────────────────────────────────────────────────────────────────────────
24
- # 1) LOW-LEVEL: "teardown_pipeline" and "teardown_index" using AsyncDeepsetClient as a context manager
25
- # ─────────────────────────────────────────────────────────────────────────────
26
-
27
-
28
- async def teardown_pipeline_async(
29
- *,
30
- pipeline_name: str,
31
- workspace_name: str,
32
- api_key: str | None = None,
33
- ) -> None:
34
- """
35
- Delete a pipeline in the given workspace.
36
-
37
- Uses DP_API_KEY or explicit api_key.
38
- """
39
- key_to_use = _get_api_key(api_key)
40
- async with AsyncDeepsetClient(api_key=key_to_use) as client:
41
- await client.pipelines(workspace=workspace_name).delete(pipeline_name)
42
- return None
43
-
44
-
45
- async def teardown_index_async(
46
- *,
47
- index_name: str,
48
- workspace_name: str,
49
- api_key: str | None = None,
50
- ) -> None:
51
- """
52
- Delete an index in the given workspace.
53
-
54
- Uses DP_API_KEY or explicit api_key.
55
- """
56
- key_to_use = _get_api_key(api_key)
57
- async with AsyncDeepsetClient(api_key=key_to_use) as client:
58
- await client.indexes(workspace=workspace_name).delete(index_name)
59
- return None
60
-
61
-
62
- # ─────────────────────────────────────────────────────────────────────────────
63
- # 2) MID-LEVEL: teardown a full test-case (pipeline + index if present)
64
- # ─────────────────────────────────────────────────────────────────────────────
65
-
66
-
67
- async def teardown_test_case_async(
68
- *,
69
- test_cfg: TestCaseConfig,
70
- workspace_name: str,
71
- api_key: str | None = None,
72
- ) -> None:
73
- """
74
- Given a TestCaseConfig, delete the index and the pipeline in the specified workspace.
75
-
76
- Uses DP_API_KEY or explicit api_key.
77
- """
78
- # 1) If there's a "query pipeline" to delete:
79
- if test_cfg.query_yaml:
80
- assert test_cfg.query_name is not None # already validated by Pydantic model; added to satisfy mypy
81
- await teardown_pipeline_async(
82
- pipeline_name=test_cfg.query_name,
83
- workspace_name=workspace_name,
84
- api_key=api_key,
85
- )
86
-
87
- # 2) If there's an index to delete:
88
- if test_cfg.index_yaml:
89
- assert test_cfg.index_name is not None # already validated by Pydantic model; added to satisfy mypy
90
- await teardown_index_async(
91
- index_name=test_cfg.index_name,
92
- workspace_name=workspace_name,
93
- api_key=api_key,
94
- )
95
-
96
- return None
97
-
98
-
99
- # ─────────────────────────────────────────────────────────────────────────────
100
- # 3) HIGH-LEVEL: parallel "teardown all" with configurable concurrency
101
- # ─────────────────────────────────────────────────────────────────────────────
102
-
103
-
104
- async def teardown_all_async(
105
- *,
106
- test_cfgs: list[TestCaseConfig],
107
- workspace_name: str,
108
- api_key: str | None = None,
109
- concurrency: int = 5,
110
- ) -> None:
111
- """
112
- Given a list of TestCaseConfig, delete all indexes and pipelines in parallel.
113
-
114
- Uses DP_API_KEY or explicit api_key.
115
- """
116
- semaphore = asyncio.Semaphore(concurrency)
117
- tasks: list[asyncio.Task[Any]] = []
118
-
119
- async def sem_task(cfg: TestCaseConfig) -> str:
120
- async with semaphore:
121
- await teardown_test_case_async(test_cfg=cfg, workspace_name=workspace_name, api_key=api_key)
122
- return cfg.name
123
-
124
- for cfg in test_cfgs:
125
- tasks.append(asyncio.create_task(sem_task(cfg)))
126
-
127
- done, _ = await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
128
- errors: list[Exception] = []
129
- for t in done:
130
- if t.exception():
131
- errors.append(t.exception()) # type: ignore
132
-
133
- if errors:
134
- raise RuntimeError(f"Errors during teardown: {errors}")
135
-
136
- return None
137
-
138
-
139
- # ─────────────────────────────────────────────────────────────────────────────
140
- # 4) SYNC WRAPPERS for all of the above (now accept api_key)
141
- # ─────────────────────────────────────────────────────────────────────────────
142
-
143
-
144
- def teardown_pipeline(
145
- *,
146
- pipeline_name: str,
147
- workspace_name: str,
148
- api_key: str | None = None,
149
- ) -> None:
150
- """Synchronous wrapper for teardown_pipeline_async. Blocks until the pipeline is deleted."""
151
- return asyncio.run(
152
- teardown_pipeline_async(
153
- pipeline_name=pipeline_name,
154
- workspace_name=workspace_name,
155
- api_key=api_key,
156
- )
157
- )
158
-
159
-
160
- def teardown_index(
161
- *,
162
- index_name: str,
163
- workspace_name: str,
164
- api_key: str | None = None,
165
- ) -> None:
166
- """Synchronous wrapper for teardown_index_async. Blocks until the index is deleted."""
167
- return asyncio.run(
168
- teardown_index_async(
169
- index_name=index_name,
170
- workspace_name=workspace_name,
171
- api_key=api_key,
172
- )
173
- )
174
-
175
-
176
- def teardown_test_case(
177
- *,
178
- test_cfg: TestCaseConfig,
179
- workspace_name: str,
180
- api_key: str | None = None,
181
- ) -> None:
182
- """Synchronous wrapper: blocks until both pipeline and index (if any) are deleted."""
183
- return asyncio.run(teardown_test_case_async(test_cfg=test_cfg, workspace_name=workspace_name, api_key=api_key))
184
-
185
-
186
- def teardown_all(
187
- *,
188
- test_cfgs: list[TestCaseConfig],
189
- workspace_name: str,
190
- api_key: str | None = None,
191
- concurrency: int = 5,
192
- ) -> None:
193
- """Synchronous wrapper for teardown_all_async. Blocks until all test-cases are deleted."""
194
- return asyncio.run(
195
- teardown_all_async(test_cfgs=test_cfgs, workspace_name=workspace_name, api_key=api_key, concurrency=concurrency)
196
- )
@@ -1,21 +0,0 @@
1
- from haystack.tracing.tracer import enable_tracing as haystack_enable_tracing, tracer
2
- from haystack_integrations.tracing.langfuse import LangfuseTracer
3
- from langfuse import Langfuse
4
-
5
-
6
- def enable_tracing(
7
- secret_key: str,
8
- public_key: str,
9
- name: str,
10
- ) -> None:
11
- """Enables tracing with langfuse."""
12
- resolved_langfuse_client_kwargs = {
13
- "secret_key": secret_key,
14
- "public_key": public_key,
15
- }
16
- tracer.is_content_tracing_enabled = True
17
- langfuse_tracer = LangfuseTracer(
18
- tracer=Langfuse(**resolved_langfuse_client_kwargs),
19
- name=name,
20
- )
21
- haystack_enable_tracing(langfuse_tracer)
@@ -1,16 +0,0 @@
1
- name: "chat_rag_answers_wrong_format"
2
- objective: "Add an AnswerBuilder and connect it to the answers output."
3
- prompt: "Can you check why my chat rag pipeline doesn't work."
4
- query_yaml: "pipelines/chat_rag_answers_wrong_format.yml"
5
- query_name: "rag-chat-test"
6
- index_yaml: "pipelines/standard_index.yml"
7
- index_name: "standard-index"
8
- tags:
9
- - "debug"
10
- - "pipeline-outputs"
11
- judge_prompt: |
12
- The Agent has:
13
- - successfully added an AnswerBuilder
14
- - connected the qa_llm.replies output to AnswerBuilder.replies
15
- - connected the query input to the AnswerBuilder
16
- - added the output of AnswerBuilder as the overall "answers" output of the pipeline
@@ -1,13 +0,0 @@
1
- name: "documents_output_wrong"
2
- objective: "Connect the retrieved documents to the documents output."
3
- prompt: "Can you check why my rag pipeline doesn't work."
4
- query_yaml: "pipelines/rag_documents_wrong_format.yml"
5
- query_name: "rag"
6
- index_yaml: "pipelines/standard_index.yml"
7
- index_name: "standard-index"
8
- tags:
9
- - "debug"
10
- - "pipeline-outputs"
11
- judge_prompt: |
12
- The Agent has:
13
- - successfully connected retrieved documents to the document output
@@ -1,11 +0,0 @@
1
- name: "jinja_str_instead_of_complex_type"
2
- objective: "Add parentheses after the filter in the OutputAdapter to get correct attribute access."
3
- prompt: "Can you check why my chat agent pipeline doesn't work."
4
- query_yaml: "pipelines/chat_agent_jinja_syntax.yml"
5
- query_name: "chat-agent"
6
- tags:
7
- - "debug"
8
- - "jinja"
9
- judge_prompt: |
10
- The Agent has:
11
- - added parentheses around messages|last so that attribute access works correctly
@@ -1,11 +0,0 @@
1
- name: "jinja_syntax_error"
2
- objective: "Add double braces to the OutputAdapter to fix the syntax error."
3
- prompt: "Can you check why my chat agent pipeline doesn't work."
4
- query_yaml: "pipelines/chat_agent_jinja_str.yml"
5
- query_name: "chat-agent"
6
- tags:
7
- - "debug"
8
- - "jinja"
9
- judge_prompt: |
10
- The Agent has:
11
- - successfully fixed the template on the OutputAdapter to not contain additional whitespace
@@ -1,14 +0,0 @@
1
- name: "missing_output_mapping"
2
- objective: "Add pipeline inputs and outputs."
3
- prompt: "Can you check why my chat rag pipeline doesn't work."
4
- query_yaml: "pipelines/chat_rag_missing_output_mapping.yml"
5
- query_name: "rag-chat-test"
6
- index_yaml: "pipelines/standard_index.yml"
7
- index_name: "standard-index"
8
- tags:
9
- - "debug"
10
- - "pipeline-outputs"
11
- judge_prompt: |
12
- The Agent has:
13
- - successfully added an outputs key
14
- - connected the document and answer outputs
@@ -1,13 +0,0 @@
1
- name: "no_query_input"
2
- objective: "Connect a query inputs to all relevant components."
3
- prompt: "Can you check why my rag pipeline doesn't work."
4
- query_yaml: "pipelines/rag_no_query_input.yml"
5
- query_name: "rag"
6
- index_yaml: "pipelines/standard_index.yml"
7
- index_name: "standard-index"
8
- tags:
9
- - "debug"
10
- - "pipeline-outputs"
11
- judge_prompt: |
12
- The Agent has:
13
- - successfully connected a query input to all retrievers, ranker and Prompt- and AnswerBuilder