deepset-mcp 0.0.3rc1__py3-none-any.whl → 0.0.4rc1__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.
- deepset_mcp/__init__.py +10 -0
- deepset_mcp/api/__init__.py +4 -0
- deepset_mcp/api/client.py +4 -0
- deepset_mcp/api/custom_components/__init__.py +4 -0
- deepset_mcp/api/custom_components/models.py +4 -0
- deepset_mcp/api/custom_components/protocols.py +4 -0
- deepset_mcp/api/custom_components/resource.py +4 -0
- deepset_mcp/api/exceptions.py +4 -0
- deepset_mcp/api/haystack_service/__init__.py +4 -0
- deepset_mcp/api/haystack_service/protocols.py +4 -0
- deepset_mcp/api/haystack_service/resource.py +4 -0
- deepset_mcp/api/indexes/__init__.py +4 -0
- deepset_mcp/api/indexes/models.py +4 -0
- deepset_mcp/api/indexes/protocols.py +4 -0
- deepset_mcp/api/indexes/resource.py +4 -0
- deepset_mcp/api/integrations/__init__.py +4 -0
- deepset_mcp/api/integrations/models.py +4 -0
- deepset_mcp/api/integrations/protocols.py +4 -0
- deepset_mcp/api/integrations/resource.py +4 -0
- deepset_mcp/api/pipeline/__init__.py +4 -0
- deepset_mcp/api/pipeline/log_level.py +4 -0
- deepset_mcp/api/pipeline/models.py +4 -0
- deepset_mcp/api/pipeline/protocols.py +8 -0
- deepset_mcp/api/pipeline/resource.py +4 -0
- deepset_mcp/api/pipeline_template/__init__.py +4 -0
- deepset_mcp/api/pipeline_template/models.py +4 -0
- deepset_mcp/api/pipeline_template/protocols.py +4 -0
- deepset_mcp/api/pipeline_template/resource.py +4 -0
- deepset_mcp/api/protocols.py +4 -0
- deepset_mcp/api/secrets/__init__.py +4 -0
- deepset_mcp/api/secrets/models.py +4 -0
- deepset_mcp/api/secrets/protocols.py +4 -0
- deepset_mcp/api/secrets/resource.py +4 -0
- deepset_mcp/api/shared_models.py +4 -0
- deepset_mcp/api/transport.py +4 -0
- deepset_mcp/api/user/__init__.py +4 -0
- deepset_mcp/api/user/protocols.py +4 -0
- deepset_mcp/api/user/resource.py +4 -0
- deepset_mcp/api/workspace/__init__.py +4 -0
- deepset_mcp/api/workspace/models.py +4 -0
- deepset_mcp/api/workspace/protocols.py +4 -0
- deepset_mcp/api/workspace/resource.py +4 -0
- deepset_mcp/config.py +8 -0
- deepset_mcp/initialize_embedding_model.py +4 -0
- deepset_mcp/main.py +8 -0
- deepset_mcp/store.py +4 -0
- deepset_mcp/tool_factory.py +11 -4
- deepset_mcp/tools/__init__.py +4 -0
- deepset_mcp/tools/custom_components.py +4 -0
- deepset_mcp/tools/doc_search.py +4 -0
- deepset_mcp/tools/haystack_service.py +4 -0
- deepset_mcp/tools/haystack_service_models.py +4 -0
- deepset_mcp/tools/indexes.py +4 -0
- deepset_mcp/tools/model_protocol.py +4 -0
- deepset_mcp/tools/pipeline.py +4 -0
- deepset_mcp/tools/pipeline_template.py +4 -0
- deepset_mcp/tools/secrets.py +4 -0
- deepset_mcp/tools/tokonomics/__init__.py +4 -0
- deepset_mcp/tools/tokonomics/decorators.py +4 -0
- deepset_mcp/tools/tokonomics/explorer.py +4 -0
- deepset_mcp/tools/tokonomics/object_store.py +4 -0
- deepset_mcp/tools/workspace.py +4 -0
- deepset_mcp-0.0.4rc1.dist-info/METADATA +761 -0
- deepset_mcp-0.0.4rc1.dist-info/RECORD +70 -0
- {deepset_mcp-0.0.3rc1.dist-info → deepset_mcp-0.0.4rc1.dist-info}/entry_points.txt +0 -1
- deepset_mcp-0.0.4rc1.dist-info/licenses/LICENSE +202 -0
- deepset_mcp/agents/__init__.py +0 -0
- deepset_mcp/agents/debugging/__init__.py +0 -0
- deepset_mcp/agents/debugging/debugging_agent.py +0 -37
- deepset_mcp/agents/debugging/system_prompt.md +0 -214
- deepset_mcp/agents/generalist/__init__.py +0 -0
- deepset_mcp/agents/generalist/generalist_agent.py +0 -38
- deepset_mcp/agents/generalist/system_prompt.md +0 -241
- deepset_mcp/benchmark/README.md +0 -425
- deepset_mcp/benchmark/__init__.py +0 -1
- deepset_mcp/benchmark/agent_configs/debugging_agent.yml +0 -10
- deepset_mcp/benchmark/agent_configs/generalist_agent.yml +0 -6
- deepset_mcp/benchmark/dp_validation_error_analysis/__init__.py +0 -0
- deepset_mcp/benchmark/dp_validation_error_analysis/eda.ipynb +0 -757
- deepset_mcp/benchmark/dp_validation_error_analysis/prepare_interaction_data.ipynb +0 -167
- deepset_mcp/benchmark/dp_validation_error_analysis/preprocessing_utils.py +0 -213
- deepset_mcp/benchmark/runner/__init__.py +0 -0
- deepset_mcp/benchmark/runner/agent_benchmark_runner.py +0 -561
- deepset_mcp/benchmark/runner/agent_loader.py +0 -110
- deepset_mcp/benchmark/runner/cli.py +0 -39
- deepset_mcp/benchmark/runner/cli_agent.py +0 -373
- deepset_mcp/benchmark/runner/cli_index.py +0 -71
- deepset_mcp/benchmark/runner/cli_pipeline.py +0 -73
- deepset_mcp/benchmark/runner/cli_tests.py +0 -226
- deepset_mcp/benchmark/runner/cli_utils.py +0 -61
- deepset_mcp/benchmark/runner/config.py +0 -73
- deepset_mcp/benchmark/runner/config_loader.py +0 -64
- deepset_mcp/benchmark/runner/interactive.py +0 -140
- deepset_mcp/benchmark/runner/models.py +0 -203
- deepset_mcp/benchmark/runner/repl.py +0 -67
- deepset_mcp/benchmark/runner/setup_actions.py +0 -238
- deepset_mcp/benchmark/runner/streaming.py +0 -360
- deepset_mcp/benchmark/runner/teardown_actions.py +0 -196
- deepset_mcp/benchmark/runner/tracing.py +0 -21
- deepset_mcp/benchmark/tasks/chat_rag_answers_wrong_format.yml +0 -16
- deepset_mcp/benchmark/tasks/documents_output_wrong.yml +0 -13
- deepset_mcp/benchmark/tasks/jinja_str_instead_of_complex_type.yml +0 -11
- deepset_mcp/benchmark/tasks/jinja_syntax_error.yml +0 -11
- deepset_mcp/benchmark/tasks/missing_output_mapping.yml +0 -14
- deepset_mcp/benchmark/tasks/no_query_input.yml +0 -13
- deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_str.yml +0 -141
- deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_syntax.yml +0 -141
- deepset_mcp/benchmark/tasks/pipelines/chat_rag_answers_wrong_format.yml +0 -181
- deepset_mcp/benchmark/tasks/pipelines/chat_rag_missing_output_mapping.yml +0 -189
- deepset_mcp/benchmark/tasks/pipelines/rag_documents_wrong_format.yml +0 -193
- deepset_mcp/benchmark/tasks/pipelines/rag_no_query_input.yml +0 -191
- deepset_mcp/benchmark/tasks/pipelines/standard_index.yml +0 -167
- deepset_mcp-0.0.3rc1.dist-info/METADATA +0 -289
- deepset_mcp-0.0.3rc1.dist-info/RECORD +0 -115
- {deepset_mcp-0.0.3rc1.dist-info → deepset_mcp-0.0.4rc1.dist-info}/WHEEL +0 -0
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Any, Self
|
|
3
|
-
|
|
4
|
-
import yaml
|
|
5
|
-
from pydantic import BaseModel, Field, PrivateAttr, ValidationError, model_validator
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestCaseConfig(BaseModel):
|
|
9
|
-
"""
|
|
10
|
-
Pydantic v2 model for a single “benchmark/tasks/<test>.yml” file.
|
|
11
|
-
|
|
12
|
-
Provides a `from_file()` constructor that:
|
|
13
|
-
- Reads the YAML from disk.
|
|
14
|
-
- Resolves `query_yaml`, `index_yaml`, `expected_query` as paths relative to the YAML file’s directory.
|
|
15
|
-
- Validates that at least one of query/index is present, that paired fields (query_name/index_name) exist,
|
|
16
|
-
and that each referenced file actually exists on disk.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
name: str = Field(
|
|
20
|
-
...,
|
|
21
|
-
description="Unique identifier for this test case (snake_case, no spaces).",
|
|
22
|
-
pattern=r"^[a-z0-9_]+$",
|
|
23
|
-
)
|
|
24
|
-
objective: str = Field(..., description="A short description of what this test is about.")
|
|
25
|
-
prompt: str = Field(..., description="The prompt text to send to the Agent.")
|
|
26
|
-
query_yaml: str | None = Field(None, description="Relative or absolute path to a query pipeline YAML.")
|
|
27
|
-
query_name: str | None = Field(
|
|
28
|
-
None, description="Name to assign to the ‘query’ pipeline if `query_yaml` is present."
|
|
29
|
-
)
|
|
30
|
-
index_yaml: str | None = Field(None, description="Relative or absolute path to an indexing pipeline YAML.")
|
|
31
|
-
index_name: str | None = Field(None, description="Name to assign to the Index if `index_yaml` is present.")
|
|
32
|
-
expected_query: str | None = Field(
|
|
33
|
-
None, description="(Optional) Relative or absolute path to a 'gold' query pipeline YAML."
|
|
34
|
-
)
|
|
35
|
-
expected_index: str | None = Field(None, description="(Optional) Relative or absolute path to a 'gold' index YAML.")
|
|
36
|
-
tags: list[str] = Field(default_factory=list, description="Tags (e.g. [api-outputs, debug]).")
|
|
37
|
-
judge_prompt: str | None = Field(
|
|
38
|
-
None,
|
|
39
|
-
description="(Optional) Prompt to use for a judge LLM to verify correctness.",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
# These PrivateAttrs will hold the raw YAML‐as‐text after reading from disk:
|
|
43
|
-
_query_yaml_text: str | None = PrivateAttr(default=None)
|
|
44
|
-
_index_yaml_text: str | None = PrivateAttr(default=None)
|
|
45
|
-
_expected_query_text: str | None = PrivateAttr(default=None)
|
|
46
|
-
_expected_index_text: str | None = PrivateAttr(default=None)
|
|
47
|
-
|
|
48
|
-
@model_validator(mode="before")
|
|
49
|
-
def _check_at_least_one(cls, values: dict[str, str]) -> dict[str, str]:
|
|
50
|
-
"""Before any field‐level validation, ensure at least one of `query_yaml` or `index_yaml` is provided."""
|
|
51
|
-
if not values.get("query_yaml") and not values.get("index_yaml"):
|
|
52
|
-
raise ValueError("At least one of `query_yaml` or `index_yaml` must be provided.")
|
|
53
|
-
return values
|
|
54
|
-
|
|
55
|
-
@model_validator(mode="after")
|
|
56
|
-
def _load_yaml_files(self) -> Self:
|
|
57
|
-
"""
|
|
58
|
-
Hook to load YAML contents from disk.
|
|
59
|
-
|
|
60
|
-
After all standard field validation has passed, this hook:
|
|
61
|
-
|
|
62
|
-
1) If `query_yaml` is set:
|
|
63
|
-
- Ensures `query_name` is also set.
|
|
64
|
-
- Reads the file from disk into `_query_yaml_text`.
|
|
65
|
-
2) If `index_yaml` is set:
|
|
66
|
-
- Ensures `index_name` is also set.
|
|
67
|
-
- Reads the file from disk into `_index_yaml_text`.
|
|
68
|
-
3) If `expected_query` is set:
|
|
69
|
-
- Reads that file into `_expected_query_text`.
|
|
70
|
-
Any missing paired field or missing file will raise an error.
|
|
71
|
-
"""
|
|
72
|
-
# 1) Query pipeline YAML → text
|
|
73
|
-
if self.query_yaml:
|
|
74
|
-
if not self.query_name:
|
|
75
|
-
raise ValueError("`query_name` must be provided if `query_yaml` is set.")
|
|
76
|
-
path = Path(self.query_yaml)
|
|
77
|
-
if not path.is_file():
|
|
78
|
-
raise FileNotFoundError(f"query_yaml file not found: {self.query_yaml}")
|
|
79
|
-
self._query_yaml_text = path.read_text(encoding="utf-8")
|
|
80
|
-
|
|
81
|
-
# 2) Index YAML → text
|
|
82
|
-
if self.index_yaml:
|
|
83
|
-
if not self.index_name:
|
|
84
|
-
raise ValueError("`index_name` must be provided if `index_yaml` is set.")
|
|
85
|
-
path = Path(self.index_yaml)
|
|
86
|
-
if not path.is_file():
|
|
87
|
-
raise FileNotFoundError(f"index_yaml file not found: {self.index_yaml}")
|
|
88
|
-
self._index_yaml_text = path.read_text(encoding="utf-8")
|
|
89
|
-
|
|
90
|
-
# 3) Expected “gold” pipeline YAML → text
|
|
91
|
-
if self.expected_query:
|
|
92
|
-
path = Path(self.expected_query)
|
|
93
|
-
if not path.is_file():
|
|
94
|
-
raise FileNotFoundError(f"expected_query file not found: {self.expected_query}")
|
|
95
|
-
self._expected_query_text = path.read_text(encoding="utf-8")
|
|
96
|
-
|
|
97
|
-
if self.expected_index:
|
|
98
|
-
path = Path(self.expected_index)
|
|
99
|
-
if not path.is_file():
|
|
100
|
-
raise FileNotFoundError(f"expected_index file not found: {self.expected_index}")
|
|
101
|
-
self._expected_index_text = path.read_text(encoding="utf-8")
|
|
102
|
-
|
|
103
|
-
return self
|
|
104
|
-
|
|
105
|
-
def get_query_yaml_text(self) -> str | None:
|
|
106
|
-
"""Return the raw text of the query‐pipeline YAML (or None if not set)."""
|
|
107
|
-
return self._query_yaml_text
|
|
108
|
-
|
|
109
|
-
def get_index_yaml_text(self) -> str | None:
|
|
110
|
-
"""Return the raw text of the index YAML (or None if not set)."""
|
|
111
|
-
return self._index_yaml_text
|
|
112
|
-
|
|
113
|
-
def get_expected_query_text(self) -> str | None:
|
|
114
|
-
"""Return the raw text of the expected “gold” pipeline YAML (or None)."""
|
|
115
|
-
return self._expected_query_text
|
|
116
|
-
|
|
117
|
-
def get_expected_index_text(self) -> str | None:
|
|
118
|
-
"""Return the raw text of the expected 'gold' index YAML (or None)."""
|
|
119
|
-
return self._expected_index_text
|
|
120
|
-
|
|
121
|
-
@classmethod
|
|
122
|
-
def from_file(cls, cfg_path: Path) -> Self:
|
|
123
|
-
"""
|
|
124
|
-
Read a test-case YAML from `cfg_path`, then initialize and return a TestCaseConfig instance.
|
|
125
|
-
|
|
126
|
-
Raises:
|
|
127
|
-
- FileNotFoundError if cfg_path doesn’t exist.
|
|
128
|
-
- ValidationError if any field is invalid or any referenced file is missing.
|
|
129
|
-
"""
|
|
130
|
-
if not cfg_path.is_file():
|
|
131
|
-
raise FileNotFoundError(f"Test-case config not found: {cfg_path}")
|
|
132
|
-
|
|
133
|
-
raw = yaml.safe_load(cfg_path.read_text(encoding="utf-8"))
|
|
134
|
-
if not isinstance(raw, dict):
|
|
135
|
-
raise ValidationError(f"Invalid test-case YAML at {cfg_path}; expected a mapping.")
|
|
136
|
-
|
|
137
|
-
base_dir = cfg_path.parent
|
|
138
|
-
|
|
139
|
-
# For each of query_yaml, index_yaml, expected_query: if present and relative, make it absolute.
|
|
140
|
-
for field_name in ("query_yaml", "index_yaml", "expected_query"):
|
|
141
|
-
p = raw.get(field_name)
|
|
142
|
-
if p:
|
|
143
|
-
# Only rewrite if it’s not already absolute
|
|
144
|
-
candidate = Path(p)
|
|
145
|
-
if not candidate.is_absolute():
|
|
146
|
-
raw[field_name] = str((base_dir / candidate).resolve())
|
|
147
|
-
|
|
148
|
-
return cls(**raw)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
class AgentConfig(BaseModel):
|
|
152
|
-
"""Agent configuration with flexible loading patterns."""
|
|
153
|
-
|
|
154
|
-
agent_json: str | None = Field(None, description="Relative or absolute path to an agent JSON file.")
|
|
155
|
-
|
|
156
|
-
agent_factory_function: str | None = Field(None, description="Qualified name of Agent factory function.")
|
|
157
|
-
|
|
158
|
-
display_name: str = Field(..., description="Display name for the agent.")
|
|
159
|
-
|
|
160
|
-
interactive: bool = Field(False, description="Whether to run the agent in interactive mode.")
|
|
161
|
-
|
|
162
|
-
required_env_vars: list[str] = Field(
|
|
163
|
-
default_factory=list, description="Required environment variables to run the agent."
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
@model_validator(mode="before")
|
|
167
|
-
@classmethod
|
|
168
|
-
def check_mutually_exclusive(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
169
|
-
"""Ensure exactly one loading method is specified."""
|
|
170
|
-
methods = [values.get("agent_json"), values.get("agent_factory_function")]
|
|
171
|
-
|
|
172
|
-
if sum(bool(m) for m in methods) != 1:
|
|
173
|
-
raise ValueError("Exactly one of agent_json or agent_factory_function must be provided")
|
|
174
|
-
return values
|
|
175
|
-
|
|
176
|
-
@model_validator(mode="after")
|
|
177
|
-
def validate_files_exist(self) -> Self:
|
|
178
|
-
"""Validate that referenced files exist."""
|
|
179
|
-
if self.agent_json:
|
|
180
|
-
json_path = Path(self.agent_json)
|
|
181
|
-
if not json_path.is_file():
|
|
182
|
-
raise FileNotFoundError(f"Agent JSON file not found: {self.agent_json}")
|
|
183
|
-
return self
|
|
184
|
-
|
|
185
|
-
@classmethod
|
|
186
|
-
def from_file(cls, cfg_path: Path) -> Self:
|
|
187
|
-
"""Read agent config from YAML file."""
|
|
188
|
-
if not cfg_path.is_file():
|
|
189
|
-
raise FileNotFoundError(f"Agent config not found: {cfg_path}")
|
|
190
|
-
|
|
191
|
-
raw = yaml.safe_load(cfg_path.read_text(encoding="utf-8"))
|
|
192
|
-
if not isinstance(raw, dict):
|
|
193
|
-
raise ValueError(f"Invalid agent config YAML at {cfg_path}; expected a mapping.")
|
|
194
|
-
|
|
195
|
-
base_dir = cfg_path.parent
|
|
196
|
-
|
|
197
|
-
# Resolve relative paths for agent_json
|
|
198
|
-
if "agent_json" in raw and raw["agent_json"]:
|
|
199
|
-
json_path = Path(raw["agent_json"])
|
|
200
|
-
if not json_path.is_absolute():
|
|
201
|
-
raw["agent_json"] = str((base_dir / json_path).resolve())
|
|
202
|
-
|
|
203
|
-
return cls(**raw)
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
import typer
|
|
5
|
-
from haystack.dataclasses.chat_message import ChatMessage
|
|
6
|
-
from haystack.dataclasses.streaming_chunk import StreamingChunk
|
|
7
|
-
|
|
8
|
-
from deepset_mcp.benchmark.runner.agent_loader import load_agent
|
|
9
|
-
from deepset_mcp.benchmark.runner.config import BenchmarkConfig
|
|
10
|
-
from deepset_mcp.benchmark.runner.models import AgentConfig
|
|
11
|
-
from deepset_mcp.benchmark.runner.streaming import StreamingCallbackManager
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
async def run_repl_session_async(agent_config: AgentConfig, benchmark_config: BenchmarkConfig) -> None:
|
|
15
|
-
"""Starts an interactive REPL session with the specified agent."""
|
|
16
|
-
agent, _ = load_agent(
|
|
17
|
-
config=agent_config,
|
|
18
|
-
benchmark_config=benchmark_config,
|
|
19
|
-
interactive=agent_config.interactive,
|
|
20
|
-
)
|
|
21
|
-
history: list[ChatMessage] = []
|
|
22
|
-
|
|
23
|
-
typer.secho(f"Starting interactive session with '{agent_config.display_name}'.", fg=typer.colors.CYAN)
|
|
24
|
-
typer.secho("Type 'exit' or 'quit' to end the session.", fg=typer.colors.CYAN)
|
|
25
|
-
|
|
26
|
-
while True:
|
|
27
|
-
try:
|
|
28
|
-
user_input = typer.prompt("\n👤 You")
|
|
29
|
-
if user_input.lower() in ["exit", "quit"]:
|
|
30
|
-
typer.secho("Ending session. Goodbye!", fg=typer.colors.MAGENTA)
|
|
31
|
-
break
|
|
32
|
-
|
|
33
|
-
# Add user message to history
|
|
34
|
-
history.append(ChatMessage.from_user(user_input))
|
|
35
|
-
|
|
36
|
-
# Setup streaming
|
|
37
|
-
streaming_callback_manager = StreamingCallbackManager()
|
|
38
|
-
|
|
39
|
-
async def streaming_callback(
|
|
40
|
-
chunk: StreamingChunk,
|
|
41
|
-
manager: StreamingCallbackManager = streaming_callback_manager,
|
|
42
|
-
) -> Any:
|
|
43
|
-
return await manager(chunk)
|
|
44
|
-
|
|
45
|
-
# Run the agent
|
|
46
|
-
typer.secho("\n🤖 Agent\n\n", fg=typer.colors.BLUE, nl=False)
|
|
47
|
-
agent_output = await agent.run_async(messages=history, streaming_callback=streaming_callback)
|
|
48
|
-
|
|
49
|
-
# The streaming callback handles printing the final text output.
|
|
50
|
-
# We replace our local history with the full history from the agent
|
|
51
|
-
# to preserve the tool calls and results.
|
|
52
|
-
if agent_output and "messages" in agent_output:
|
|
53
|
-
history = agent_output["messages"]
|
|
54
|
-
|
|
55
|
-
except (KeyboardInterrupt, EOFError):
|
|
56
|
-
typer.secho("\nEnding session. Goodbye!", fg=typer.colors.MAGENTA)
|
|
57
|
-
break
|
|
58
|
-
except Exception as e:
|
|
59
|
-
typer.secho(f"\nAn error occurred: {e}", fg=typer.colors.RED)
|
|
60
|
-
# Remove the user message that caused the error to prevent it from being re-processed
|
|
61
|
-
if history and history[-1].is_from("user"):
|
|
62
|
-
history.pop()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def run_repl_session(agent_config: AgentConfig, benchmark_config: BenchmarkConfig) -> None:
|
|
66
|
-
"""Synchronous wrapper for the REPL session."""
|
|
67
|
-
asyncio.run(run_repl_session_async(agent_config, benchmark_config))
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import os
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from deepset_mcp.api.client import AsyncDeepsetClient
|
|
7
|
-
from deepset_mcp.benchmark.runner.models import TestCaseConfig
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _get_api_key(explicit_key: str | None) -> str:
|
|
11
|
-
"""
|
|
12
|
-
Return whichever API key to use: explicit_key takes precedence, otherwise read DP_API_KEY from the environment.
|
|
13
|
-
|
|
14
|
-
If still missing, raise ValueError.
|
|
15
|
-
"""
|
|
16
|
-
if explicit_key:
|
|
17
|
-
return explicit_key
|
|
18
|
-
env_key = os.getenv("DP_API_KEY")
|
|
19
|
-
if not env_key:
|
|
20
|
-
raise ValueError("No API key provided: pass --api-key or set DP_API_KEY in env.")
|
|
21
|
-
return env_key
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
-
# 1) LOW-LEVEL: “setup_pipeline” and “setup_index” using AsyncDeepsetClient as a context manager
|
|
26
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
async def setup_pipeline_async(
|
|
30
|
-
*,
|
|
31
|
-
yaml_path: str | None = None,
|
|
32
|
-
yaml_content: str | None = None,
|
|
33
|
-
pipeline_name: str,
|
|
34
|
-
workspace_name: str,
|
|
35
|
-
api_key: str | None = None,
|
|
36
|
-
) -> None:
|
|
37
|
-
"""
|
|
38
|
-
Create a new pipeline in the given workspace. Exactly one of (yaml_path, yaml_content) must be provided.
|
|
39
|
-
|
|
40
|
-
Uses DP_API_KEY or explicit api_key.
|
|
41
|
-
"""
|
|
42
|
-
if (yaml_path and yaml_content) or (not yaml_path and not yaml_content):
|
|
43
|
-
raise ValueError("Exactly one of `yaml_path` or `yaml_content` must be specified.")
|
|
44
|
-
|
|
45
|
-
if yaml_path is not None:
|
|
46
|
-
yaml_str = Path(yaml_path).read_text(encoding="utf-8")
|
|
47
|
-
else:
|
|
48
|
-
yaml_str = yaml_content # type: ignore
|
|
49
|
-
|
|
50
|
-
key_to_use = _get_api_key(api_key)
|
|
51
|
-
async with AsyncDeepsetClient(api_key=key_to_use) as client:
|
|
52
|
-
await client.pipelines(workspace=workspace_name).create(
|
|
53
|
-
name=pipeline_name,
|
|
54
|
-
yaml_config=yaml_str,
|
|
55
|
-
)
|
|
56
|
-
return None
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
async def setup_index_async(
|
|
60
|
-
*,
|
|
61
|
-
yaml_path: str | None = None,
|
|
62
|
-
yaml_content: str | None = None,
|
|
63
|
-
index_name: str,
|
|
64
|
-
workspace_name: str,
|
|
65
|
-
api_key: str | None = None,
|
|
66
|
-
description: str | None = None,
|
|
67
|
-
) -> None:
|
|
68
|
-
"""
|
|
69
|
-
Create a new index in the given workspace. Exactly one of (yaml_path, yaml_content) must be provided.
|
|
70
|
-
|
|
71
|
-
Uses DP_API_KEY or explicit api_key.
|
|
72
|
-
"""
|
|
73
|
-
if (yaml_path and yaml_content) or (not yaml_path and not yaml_content):
|
|
74
|
-
raise ValueError("Exactly one of `yaml_path` or `yaml_content` must be specified.")
|
|
75
|
-
|
|
76
|
-
if yaml_path is not None:
|
|
77
|
-
yaml_str = Path(yaml_path).read_text(encoding="utf-8")
|
|
78
|
-
else:
|
|
79
|
-
yaml_str = yaml_content # type: ignore
|
|
80
|
-
|
|
81
|
-
key_to_use = _get_api_key(api_key)
|
|
82
|
-
async with AsyncDeepsetClient(api_key=key_to_use) as client:
|
|
83
|
-
await client.indexes(workspace=workspace_name).create(
|
|
84
|
-
name=index_name,
|
|
85
|
-
yaml_config=yaml_str,
|
|
86
|
-
description=description or "",
|
|
87
|
-
)
|
|
88
|
-
return None
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
-
# 2) MID-LEVEL: setup a full test-case (pipeline + index if present)
|
|
93
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
async def setup_test_case_async(
|
|
97
|
-
*,
|
|
98
|
-
test_cfg: TestCaseConfig,
|
|
99
|
-
workspace_name: str,
|
|
100
|
-
api_key: str | None = None,
|
|
101
|
-
) -> None:
|
|
102
|
-
"""
|
|
103
|
-
Given a TestCaseConfig, create the index and the pipeline in the specified workspace.
|
|
104
|
-
|
|
105
|
-
Uses DP_API_KEY or explicit api_key.
|
|
106
|
-
"""
|
|
107
|
-
# 1) If there’s an index to create:
|
|
108
|
-
if test_cfg.index_yaml:
|
|
109
|
-
assert test_cfg.index_name is not None # already validated by Pydantic model; added to satisfy mypy
|
|
110
|
-
await setup_index_async(
|
|
111
|
-
yaml_content=test_cfg.get_index_yaml_text(),
|
|
112
|
-
index_name=test_cfg.index_name,
|
|
113
|
-
workspace_name=workspace_name,
|
|
114
|
-
api_key=api_key,
|
|
115
|
-
description=f"Index for test {test_cfg.name}",
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
# 2) If there’s a “query pipeline” to create:
|
|
119
|
-
if test_cfg.query_yaml:
|
|
120
|
-
assert test_cfg.query_name is not None # already validated by Pydantic model; added to satisfy mypy
|
|
121
|
-
await setup_pipeline_async(
|
|
122
|
-
yaml_content=test_cfg.get_query_yaml_text(),
|
|
123
|
-
pipeline_name=test_cfg.query_name,
|
|
124
|
-
workspace_name=workspace_name,
|
|
125
|
-
api_key=api_key,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
return None
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
132
|
-
# 3) HIGH-LEVEL: parallel “setup all” with configurable concurrency
|
|
133
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
async def setup_all_async(
|
|
137
|
-
*,
|
|
138
|
-
test_cfgs: list[TestCaseConfig],
|
|
139
|
-
workspace_name: str,
|
|
140
|
-
api_key: str | None = None,
|
|
141
|
-
concurrency: int = 5,
|
|
142
|
-
) -> None:
|
|
143
|
-
"""
|
|
144
|
-
Given a list of TestCaseConfig, create all indexes and pipelines in parallel.
|
|
145
|
-
|
|
146
|
-
Uses DP_API_KEY or explicit api_key.
|
|
147
|
-
"""
|
|
148
|
-
semaphore = asyncio.Semaphore(concurrency)
|
|
149
|
-
tasks: list[asyncio.Task[Any]] = []
|
|
150
|
-
|
|
151
|
-
async def sem_task(cfg: TestCaseConfig) -> str:
|
|
152
|
-
async with semaphore:
|
|
153
|
-
await setup_test_case_async(test_cfg=cfg, workspace_name=workspace_name, api_key=api_key)
|
|
154
|
-
return cfg.name
|
|
155
|
-
|
|
156
|
-
for cfg in test_cfgs:
|
|
157
|
-
tasks.append(asyncio.create_task(sem_task(cfg)))
|
|
158
|
-
|
|
159
|
-
done, _ = await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
|
|
160
|
-
errors: list[Exception] = []
|
|
161
|
-
for t in done:
|
|
162
|
-
if t.exception():
|
|
163
|
-
errors.append(t.exception()) # type: ignore
|
|
164
|
-
|
|
165
|
-
if errors:
|
|
166
|
-
raise RuntimeError(f"Errors during setup: {errors}")
|
|
167
|
-
|
|
168
|
-
return None
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
-
# 4) SYNC WRAPPERS for all of the above (now accept api_key)
|
|
173
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
def setup_pipeline(
|
|
177
|
-
*,
|
|
178
|
-
yaml_path: str | None = None,
|
|
179
|
-
yaml_content: str | None = None,
|
|
180
|
-
pipeline_name: str,
|
|
181
|
-
workspace_name: str,
|
|
182
|
-
api_key: str | None = None,
|
|
183
|
-
) -> None:
|
|
184
|
-
"""Synchronous wrapper for setup_pipeline_async. Blocks until the pipeline is created."""
|
|
185
|
-
return asyncio.run(
|
|
186
|
-
setup_pipeline_async(
|
|
187
|
-
yaml_path=yaml_path,
|
|
188
|
-
yaml_content=yaml_content,
|
|
189
|
-
pipeline_name=pipeline_name,
|
|
190
|
-
workspace_name=workspace_name,
|
|
191
|
-
api_key=api_key,
|
|
192
|
-
)
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def setup_index(
|
|
197
|
-
*,
|
|
198
|
-
yaml_path: str | None = None,
|
|
199
|
-
yaml_content: str | None = None,
|
|
200
|
-
index_name: str,
|
|
201
|
-
workspace_name: str,
|
|
202
|
-
api_key: str | None = None,
|
|
203
|
-
description: str | None = None,
|
|
204
|
-
) -> None:
|
|
205
|
-
"""Synchronous wrapper for setup_index_async. Blocks until the index is created."""
|
|
206
|
-
return asyncio.run(
|
|
207
|
-
setup_index_async(
|
|
208
|
-
yaml_path=yaml_path,
|
|
209
|
-
yaml_content=yaml_content,
|
|
210
|
-
index_name=index_name,
|
|
211
|
-
workspace_name=workspace_name,
|
|
212
|
-
api_key=api_key,
|
|
213
|
-
description=description,
|
|
214
|
-
)
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def setup_test_case(
|
|
219
|
-
*,
|
|
220
|
-
test_cfg: TestCaseConfig,
|
|
221
|
-
workspace_name: str,
|
|
222
|
-
api_key: str | None = None,
|
|
223
|
-
) -> None:
|
|
224
|
-
"""Synchronous wrapper: blocks until both pipeline and index (if any) are created."""
|
|
225
|
-
return asyncio.run(setup_test_case_async(test_cfg=test_cfg, workspace_name=workspace_name, api_key=api_key))
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def setup_all(
|
|
229
|
-
*,
|
|
230
|
-
test_cfgs: list[TestCaseConfig],
|
|
231
|
-
workspace_name: str,
|
|
232
|
-
api_key: str | None = None,
|
|
233
|
-
concurrency: int = 5,
|
|
234
|
-
) -> None:
|
|
235
|
-
"""Synchronous wrapper for setup_all_async. Blocks until all test-cases are created."""
|
|
236
|
-
return asyncio.run(
|
|
237
|
-
setup_all_async(test_cfgs=test_cfgs, workspace_name=workspace_name, api_key=api_key, concurrency=concurrency)
|
|
238
|
-
)
|