haystack-enterprise-sdk 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/PKG-INFO +31 -10
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/README.md +23 -7
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/README.md +1 -1
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/deployments.py +59 -1
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/haystack_enterprise_api.py +16 -4
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_service/deployment_service.py +55 -1
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_service/pipeline_extract.py +47 -2
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/cli.py +76 -5
- haystack_enterprise_sdk-0.2.0/haystack_enterprise_sdk/py.typed +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/async_client/deployment_client.py +26 -4
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/sync_client/deployment_client.py +13 -1
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/pyproject.toml +18 -5
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/pyproject.toml.orig +18 -5
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/LICENSE +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/__init__.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/config.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/files.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/pipeline_run.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/shared_prototypes.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_api/upload_sessions.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_console.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_s3/__init__.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_s3/upload.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_service/files_service.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_service/io_spec.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_service/pipeline_service.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_service/pipeline_transform.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_utils/__init__.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_utils/datetime.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/models.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/__init__.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/async_client/__init__.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/async_client/async_pipeline_client.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/async_client/files.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/sync_client/__init__.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/sync_client/files.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/sync_client/pipeline_client.py +0 -0
- {haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/workflows/sync_client/utils.py +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haystack-enterprise-sdk
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Haystack Enterprise
|
|
5
|
-
Keywords:
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK and CLI for the Haystack Enterprise Platform
|
|
5
|
+
Keywords: agents,deepset,haystack,llm,pipelines,rag,sdk
|
|
6
6
|
Author: deepset.ai
|
|
7
7
|
Author-email: deepset.ai <malte.pietsch@deepset.ai>
|
|
8
8
|
License-Expression: Apache-2.0
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
11
14
|
Classifier: Programming Language :: Python
|
|
12
15
|
Classifier: Programming Language :: Python :: 3
|
|
13
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -15,6 +18,8 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Typing :: Typed
|
|
18
23
|
Requires-Dist: structlog>=24.0.0
|
|
19
24
|
Requires-Dist: httpx>=0.27.2
|
|
20
25
|
Requires-Dist: python-dotenv>=1.0.1
|
|
@@ -53,14 +58,30 @@ Python SDK and CLI for the Haystack Enterprise Platform.
|
|
|
53
58
|
|
|
54
59
|
## Installation
|
|
55
60
|
|
|
56
|
-
Not published to a package registry yet — install directly from this repository with [uv](https://docs.astral.sh/uv/):
|
|
57
|
-
|
|
58
61
|
```bash
|
|
59
62
|
# Install as a CLI tool
|
|
60
|
-
uv tool install
|
|
63
|
+
uv tool install haystack-enterprise-sdk
|
|
61
64
|
|
|
62
65
|
# Or add it as a dependency of your project
|
|
63
|
-
uv add
|
|
66
|
+
uv add haystack-enterprise-sdk
|
|
67
|
+
|
|
68
|
+
# pip works too
|
|
69
|
+
pip install haystack-enterprise-sdk
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`deploy`, `validate`, and `run` load your pipeline in a subprocess using your project's own interpreter
|
|
73
|
+
(an auto-detected venv, or `--python`), so the CLI environment does not need Haystack. Install the
|
|
74
|
+
`deploy` extra only when the CLI environment doubles as the pipeline environment — that is, when there
|
|
75
|
+
is no separate project venv to detect:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv tool install "haystack-enterprise-sdk[deploy]"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
To install unreleased changes from `main`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
uv tool install git+https://github.com/deepset-ai/haystack-enterprise-sdk.git
|
|
64
85
|
```
|
|
65
86
|
|
|
66
87
|
## Usage
|
|
@@ -124,14 +145,14 @@ A deployed service is served over an OpenAI-compatible chat-completions endpoint
|
|
|
124
145
|
once the service is running:
|
|
125
146
|
|
|
126
147
|
```bash
|
|
127
|
-
curl -N https://api.cloud.deepset.ai/api/v1/workspaces/<workspace>/deployments
|
|
148
|
+
curl -N https://api.cloud.deepset.ai/api/v1/workspaces/<workspace>/deployments/v1/chat/completions \
|
|
128
149
|
-H "Authorization: Bearer $API_KEY" \
|
|
129
150
|
-H "Content-Type: application/json" \
|
|
130
|
-
-d '{"model": "<workspace>/<
|
|
151
|
+
-d '{"model": "<workspace>/<deployment-id>", "messages": [{"role": "user", "content": "Hello"}]}'
|
|
131
152
|
```
|
|
132
153
|
|
|
133
154
|
The response is a server-sent-event stream of `chat.completion.chunk` objects. Any OpenAI client works —
|
|
134
|
-
point its `base_url` at everything up to and including `/deployments
|
|
155
|
+
point its `base_url` at everything up to and including `/deployments/v1`.
|
|
135
156
|
|
|
136
157
|
Pass `--verbose` to any command for INFO/DEBUG logs, and `<command> --help` for its arguments.
|
|
137
158
|
|
|
@@ -13,14 +13,30 @@ Python SDK and CLI for the Haystack Enterprise Platform.
|
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
|
-
Not published to a package registry yet — install directly from this repository with [uv](https://docs.astral.sh/uv/):
|
|
17
|
-
|
|
18
16
|
```bash
|
|
19
17
|
# Install as a CLI tool
|
|
20
|
-
uv tool install
|
|
18
|
+
uv tool install haystack-enterprise-sdk
|
|
21
19
|
|
|
22
20
|
# Or add it as a dependency of your project
|
|
23
|
-
uv add
|
|
21
|
+
uv add haystack-enterprise-sdk
|
|
22
|
+
|
|
23
|
+
# pip works too
|
|
24
|
+
pip install haystack-enterprise-sdk
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`deploy`, `validate`, and `run` load your pipeline in a subprocess using your project's own interpreter
|
|
28
|
+
(an auto-detected venv, or `--python`), so the CLI environment does not need Haystack. Install the
|
|
29
|
+
`deploy` extra only when the CLI environment doubles as the pipeline environment — that is, when there
|
|
30
|
+
is no separate project venv to detect:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv tool install "haystack-enterprise-sdk[deploy]"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
To install unreleased changes from `main`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv tool install git+https://github.com/deepset-ai/haystack-enterprise-sdk.git
|
|
24
40
|
```
|
|
25
41
|
|
|
26
42
|
## Usage
|
|
@@ -84,14 +100,14 @@ A deployed service is served over an OpenAI-compatible chat-completions endpoint
|
|
|
84
100
|
once the service is running:
|
|
85
101
|
|
|
86
102
|
```bash
|
|
87
|
-
curl -N https://api.cloud.deepset.ai/api/v1/workspaces/<workspace>/deployments
|
|
103
|
+
curl -N https://api.cloud.deepset.ai/api/v1/workspaces/<workspace>/deployments/v1/chat/completions \
|
|
88
104
|
-H "Authorization: Bearer $API_KEY" \
|
|
89
105
|
-H "Content-Type: application/json" \
|
|
90
|
-
-d '{"model": "<workspace>/<
|
|
106
|
+
-d '{"model": "<workspace>/<deployment-id>", "messages": [{"role": "user", "content": "Hello"}]}'
|
|
91
107
|
```
|
|
92
108
|
|
|
93
109
|
The response is a server-sent-event stream of `chat.completion.chunk` objects. Any OpenAI client works —
|
|
94
|
-
point its `base_url` at everything up to and including `/deployments
|
|
110
|
+
point its `base_url` at everything up to and including `/deployments/v1`.
|
|
95
111
|
|
|
96
112
|
Pass `--verbose` to any command for INFO/DEBUG logs, and `<command> --help` for its arguments.
|
|
97
113
|
|
|
@@ -10,7 +10,7 @@ list endpoint and matching client-side.
|
|
|
10
10
|
|
|
11
11
|
import asyncio
|
|
12
12
|
import enum
|
|
13
|
-
from dataclasses import dataclass
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
14
|
from typing import Any, Dict, List, Optional, Type, TypeVar
|
|
15
15
|
from uuid import UUID
|
|
16
16
|
|
|
@@ -97,6 +97,10 @@ class Deployment:
|
|
|
97
97
|
# revision, so it stays None until a revision is activated. This is the platform's own answer to
|
|
98
98
|
# "is this a chat pipeline?" -- the CLI reads it rather than guessing from components or sockets.
|
|
99
99
|
output_type: Optional[PipelineOutputType] = None
|
|
100
|
+
# Free-form labels, capped server-side at MAX_SERVICE_TAGS (3). Not settable at creation --
|
|
101
|
+
# the platform has no `tags` field on its create-deployment request, only the dedicated
|
|
102
|
+
# add/remove-tag endpoints below, so this always reflects a create followed by N tag calls.
|
|
103
|
+
tags: List[str] = field(default_factory=list)
|
|
100
104
|
|
|
101
105
|
@classmethod
|
|
102
106
|
def from_response(cls, body: Dict[str, Any]) -> "Deployment":
|
|
@@ -120,6 +124,7 @@ class Deployment:
|
|
|
120
124
|
# The platform enum has values this SDK does not model (e.g. "unknown"), and the field is
|
|
121
125
|
# absent until a revision is active, so anything unrecognized degrades to None.
|
|
122
126
|
output_type=_enum_or_none(PipelineOutputType, body.get("output_type")),
|
|
127
|
+
tags=list(body.get("tags") or []),
|
|
123
128
|
)
|
|
124
129
|
|
|
125
130
|
|
|
@@ -244,6 +249,15 @@ class FailedToActivateRevisionError(Exception):
|
|
|
244
249
|
"""Raised when a deployment revision could not be activated."""
|
|
245
250
|
|
|
246
251
|
|
|
252
|
+
class FailedToTagDeploymentError(Exception):
|
|
253
|
+
"""Raised when a tag could not be added to, or removed from, a deployment.
|
|
254
|
+
|
|
255
|
+
Covers the platform's own rejections too: a name over 50 chars or outside
|
|
256
|
+
``[A-Za-z0-9 _-]``, a duplicate (case-insensitive), or a fourth tag past MAX_SERVICE_TAGS (3)
|
|
257
|
+
all come back as a 4xx from the tag endpoints, not just a network/auth failure.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
|
|
247
261
|
class FailedToValidatePipelineError(Exception):
|
|
248
262
|
"""Raised when the validation request itself failed (e.g. auth/5xx), not the pipeline config."""
|
|
249
263
|
|
|
@@ -453,6 +467,50 @@ class DeploymentsAPI:
|
|
|
453
467
|
)
|
|
454
468
|
return Deployment.from_response(response.json())
|
|
455
469
|
|
|
470
|
+
async def add_tag(self, workspace_name: str, deployment_id: UUID, tag_name: str) -> List[str]:
|
|
471
|
+
"""Add a tag to a deployment.
|
|
472
|
+
|
|
473
|
+
:param workspace_name: Name of the workspace.
|
|
474
|
+
:param deployment_id: Deployment id.
|
|
475
|
+
:param tag_name: Tag name (1-50 chars; letters, digits, spaces, underscores, hyphens).
|
|
476
|
+
Rejected as a duplicate case-insensitively, and past the third tag on one deployment.
|
|
477
|
+
:raises FailedToTagDeploymentError: If the tag could not be added.
|
|
478
|
+
:return: The deployment's full tag list after the add.
|
|
479
|
+
"""
|
|
480
|
+
response = await self._haystack_enterprise_api.post(
|
|
481
|
+
workspace_name=workspace_name,
|
|
482
|
+
endpoint=f"{self._ENDPOINT}/{deployment_id}/tags",
|
|
483
|
+
json={"name": tag_name},
|
|
484
|
+
)
|
|
485
|
+
raise_for_unexpected_status(
|
|
486
|
+
response,
|
|
487
|
+
(codes.OK,),
|
|
488
|
+
FailedToTagDeploymentError,
|
|
489
|
+
f"Failed to add tag {tag_name!r} to deployment '{deployment_id}'.",
|
|
490
|
+
)
|
|
491
|
+
return list(response.json())
|
|
492
|
+
|
|
493
|
+
async def remove_tag(self, workspace_name: str, deployment_id: UUID, tag_name: str) -> List[str]:
|
|
494
|
+
"""Remove a tag from a deployment. Matching is case-insensitive.
|
|
495
|
+
|
|
496
|
+
:param workspace_name: Name of the workspace.
|
|
497
|
+
:param deployment_id: Deployment id.
|
|
498
|
+
:param tag_name: Tag name to remove.
|
|
499
|
+
:raises FailedToTagDeploymentError: If the tag could not be removed (including "not found").
|
|
500
|
+
:return: The deployment's full tag list after the removal.
|
|
501
|
+
"""
|
|
502
|
+
response = await self._haystack_enterprise_api.delete(
|
|
503
|
+
workspace_name=workspace_name,
|
|
504
|
+
endpoint=f"{self._ENDPOINT}/{deployment_id}/tags/{tag_name}",
|
|
505
|
+
)
|
|
506
|
+
raise_for_unexpected_status(
|
|
507
|
+
response,
|
|
508
|
+
(codes.OK,),
|
|
509
|
+
FailedToTagDeploymentError,
|
|
510
|
+
f"Failed to remove tag {tag_name!r} from deployment '{deployment_id}'.",
|
|
511
|
+
)
|
|
512
|
+
return list(response.json())
|
|
513
|
+
|
|
456
514
|
async def validate_pipeline(
|
|
457
515
|
self,
|
|
458
516
|
workspace_name: str,
|
|
@@ -148,11 +148,9 @@ def raise_for_unexpected_status(
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def deployment_base_url(api_url: str, workspace_name: str, deployment_id: Any) -> str:
|
|
151
|
-
"""Build the base URL
|
|
151
|
+
"""Build the per-deployment base URL (used for per-deployment routes like ``/chat`` and ``/chat-stream``).
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
keyed on the deployment id, not the service name. Returned without the ``/chat/completions`` suffix
|
|
155
|
-
because that is exactly the ``base_url`` an OpenAI client expects (it appends the path itself).
|
|
153
|
+
Returned without a trailing path segment because callers append their own route (e.g. ``/chat``).
|
|
156
154
|
|
|
157
155
|
:param api_url: Base API URL, already normalized (see :func:`~haystack_enterprise_sdk._api.config.normalize_base_url`).
|
|
158
156
|
:param workspace_name: Name of the workspace the deployment lives in.
|
|
@@ -162,6 +160,20 @@ def deployment_base_url(api_url: str, workspace_name: str, deployment_id: Any) -
|
|
|
162
160
|
return f"{api_url}/{API_VERSION_PATH}/workspaces/{workspace_name}/deployments/{deployment_id}"
|
|
163
161
|
|
|
164
162
|
|
|
163
|
+
def chat_completions_base_url(api_url: str, workspace_name: str) -> str:
|
|
164
|
+
"""Build the workspace-scoped OpenAI-compatible chat-completions base URL.
|
|
165
|
+
|
|
166
|
+
The platform's OpenAI-compatible gateway lives at ``POST <this>/chat/completions``.
|
|
167
|
+
The ``model`` field must be ``{workspace_name}/{deployment_id}`` (UUID, not service name).
|
|
168
|
+
Returned without the ``/chat/completions`` suffix so an OpenAI client can use it as ``base_url``.
|
|
169
|
+
|
|
170
|
+
:param api_url: Base API URL, already normalized (see :func:`~haystack_enterprise_sdk._api.config.normalize_base_url`).
|
|
171
|
+
:param workspace_name: Name of the workspace.
|
|
172
|
+
:return: e.g. ``https://api.cloud.deepset.ai/api/v1/workspaces/my-ws/deployments/v1``.
|
|
173
|
+
"""
|
|
174
|
+
return f"{api_url}/{API_VERSION_PATH}/workspaces/{workspace_name}/deployments/v1"
|
|
175
|
+
|
|
176
|
+
|
|
165
177
|
class HaystackEnterpriseAPI:
|
|
166
178
|
"""Haystack Enterprise Platform API client.
|
|
167
179
|
|
|
@@ -22,6 +22,7 @@ from haystack_enterprise_sdk._api.deployments import (
|
|
|
22
22
|
DeploymentsAPI,
|
|
23
23
|
DeploymentServiceLevel,
|
|
24
24
|
DeploymentStatus,
|
|
25
|
+
FailedToTagDeploymentError,
|
|
25
26
|
PipelineValidationError,
|
|
26
27
|
PipelineValidationResult,
|
|
27
28
|
)
|
|
@@ -147,6 +148,9 @@ class CreateOptions:
|
|
|
147
148
|
cpu_limit: Optional[str] = None
|
|
148
149
|
memory_limit: Optional[str] = None
|
|
149
150
|
gpu_limit_gigabyte: Optional[int] = None
|
|
151
|
+
# Applied with one add_tag call each, right after creation -- the create-deployment endpoint
|
|
152
|
+
# itself has no `tags` field. Not a sizing field: it applies to serverless services too.
|
|
153
|
+
tags: Tuple[str, ...] = ()
|
|
150
154
|
|
|
151
155
|
_SIZING_FIELDS = (
|
|
152
156
|
"service_level",
|
|
@@ -493,6 +497,38 @@ class DeploymentService:
|
|
|
493
497
|
)
|
|
494
498
|
return await self._deployments.get_deployment(self._workspace_name, deployment.deployment_id)
|
|
495
499
|
|
|
500
|
+
async def add_tag(self, service_name: str, tag_name: str) -> List[str]:
|
|
501
|
+
"""Add a tag to a service by name, independent of deploy/create.
|
|
502
|
+
|
|
503
|
+
:param service_name: Name of the service deployment.
|
|
504
|
+
:param tag_name: Tag name (1-50 chars; letters, digits, spaces, underscores, hyphens).
|
|
505
|
+
:raises ServiceNotFoundError: If no service with that name exists.
|
|
506
|
+
:raises FailedToTagDeploymentError: If the platform rejected it (limit, duplicate, bad name).
|
|
507
|
+
:return: The service's full tag list after the add.
|
|
508
|
+
"""
|
|
509
|
+
deployment = await self._deployments.find_by_name(self._workspace_name, service_name)
|
|
510
|
+
if deployment is None:
|
|
511
|
+
raise ServiceNotFoundError(
|
|
512
|
+
f"No service deployment named '{service_name}' in workspace '{self._workspace_name}'."
|
|
513
|
+
)
|
|
514
|
+
return await self._deployments.add_tag(self._workspace_name, deployment.deployment_id, tag_name)
|
|
515
|
+
|
|
516
|
+
async def remove_tag(self, service_name: str, tag_name: str) -> List[str]:
|
|
517
|
+
"""Remove a tag from a service by name. Matching is case-insensitive.
|
|
518
|
+
|
|
519
|
+
:param service_name: Name of the service deployment.
|
|
520
|
+
:param tag_name: Tag name to remove.
|
|
521
|
+
:raises ServiceNotFoundError: If no service with that name exists.
|
|
522
|
+
:raises FailedToTagDeploymentError: If the tag could not be removed (including "not found").
|
|
523
|
+
:return: The service's full tag list after the removal.
|
|
524
|
+
"""
|
|
525
|
+
deployment = await self._deployments.find_by_name(self._workspace_name, service_name)
|
|
526
|
+
if deployment is None:
|
|
527
|
+
raise ServiceNotFoundError(
|
|
528
|
+
f"No service deployment named '{service_name}' in workspace '{self._workspace_name}'."
|
|
529
|
+
)
|
|
530
|
+
return await self._deployments.remove_tag(self._workspace_name, deployment.deployment_id, tag_name)
|
|
531
|
+
|
|
496
532
|
async def create_shared_prototype(
|
|
497
533
|
self, service_name: str, options: Optional[ShareOptions] = None
|
|
498
534
|
) -> SharedPrototype:
|
|
@@ -538,7 +574,7 @@ class DeploymentService:
|
|
|
538
574
|
)
|
|
539
575
|
options = create_options or CreateOptions()
|
|
540
576
|
logger.info("Creating service deployment.", service=service_name, mode=options.deployment_mode.value)
|
|
541
|
-
|
|
577
|
+
deployment = await self._deployments.create_deployment(
|
|
542
578
|
self._workspace_name,
|
|
543
579
|
name=service_name,
|
|
544
580
|
deployment_mode=options.deployment_mode,
|
|
@@ -550,6 +586,24 @@ class DeploymentService:
|
|
|
550
586
|
memory_limit=options.memory_limit,
|
|
551
587
|
gpu_limit_gigabyte=options.gpu_limit_gigabyte,
|
|
552
588
|
)
|
|
589
|
+
for tag_name in options.tags:
|
|
590
|
+
# Sequential, not gathered: each call returns the full tag list so far, and the
|
|
591
|
+
# platform's own duplicate/limit checks are per-request -- gathering could race two
|
|
592
|
+
# adds past the 3-tag cap. There are at most 3 of these, so latency is not a concern.
|
|
593
|
+
#
|
|
594
|
+
# Caught here rather than left to propagate: the service was already created, so raising
|
|
595
|
+
# would abort a deploy that otherwise fully succeeded. `tag-add` can retry independently.
|
|
596
|
+
try:
|
|
597
|
+
deployment.tags = await self._deployments.add_tag(
|
|
598
|
+
self._workspace_name, deployment.deployment_id, tag_name
|
|
599
|
+
)
|
|
600
|
+
except FailedToTagDeploymentError:
|
|
601
|
+
logger.warning(
|
|
602
|
+
"Failed to add tag to newly created service; retry with 'tag-add'.",
|
|
603
|
+
service=service_name,
|
|
604
|
+
tag=tag_name,
|
|
605
|
+
)
|
|
606
|
+
return deployment
|
|
553
607
|
|
|
554
608
|
async def _poll_until_settled(
|
|
555
609
|
self,
|
|
@@ -33,6 +33,7 @@ import json
|
|
|
33
33
|
import logging
|
|
34
34
|
import os
|
|
35
35
|
import sys
|
|
36
|
+
import sysconfig
|
|
36
37
|
from pathlib import Path
|
|
37
38
|
from typing import Any, Optional
|
|
38
39
|
|
|
@@ -53,6 +54,21 @@ _INDEX_MARKER_SUFFIXES = ("DocumentWriter",)
|
|
|
53
54
|
# Path components that mark an installed package (even when the venv lives inside the project dir).
|
|
54
55
|
_INSTALLED_PACKAGE_DIRS = {"site-packages", "dist-packages"}
|
|
55
56
|
|
|
57
|
+
# Namespaces the platform runtime provides itself. Never inline these, even when they resolve to a
|
|
58
|
+
# source checkout instead of site-packages (an editable ``haystack-ai`` install, for example).
|
|
59
|
+
_PLATFORM_NAMESPACES = ("haystack", "haystack_integrations", "deepset_cloud_custom_nodes")
|
|
60
|
+
|
|
61
|
+
# Directories owned by the interpreter running this extractor: its stdlib and its installed packages.
|
|
62
|
+
_INTERPRETER_DIRS = frozenset(
|
|
63
|
+
Path(p).resolve()
|
|
64
|
+
for p in (
|
|
65
|
+
sysconfig.get_paths()["stdlib"],
|
|
66
|
+
sysconfig.get_paths()["platstdlib"],
|
|
67
|
+
sys.prefix,
|
|
68
|
+
sys.base_prefix,
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
56
72
|
|
|
57
73
|
class PipelineTransformError(Exception):
|
|
58
74
|
"""Raised when the pipeline cannot be loaded or transformed into deployable YAML."""
|
|
@@ -277,9 +293,20 @@ def _reject_index_pipeline(pipeline: Any) -> None:
|
|
|
277
293
|
# Import classification
|
|
278
294
|
# --------------------------------------------------------------------------- #
|
|
279
295
|
def classify_module(module_name: str, project_root: Path) -> str:
|
|
280
|
-
"""Classify ``module_name`` as ``'local'``, ``'external'``, or ``'stdlib'``.
|
|
296
|
+
"""Classify ``module_name`` as ``'local'``, ``'external'``, or ``'stdlib'``.
|
|
297
|
+
|
|
298
|
+
Only ``'local'`` is acted on (it means "inline this into a platform Code component"); the other two
|
|
299
|
+
are equivalent "leave it alone" answers.
|
|
300
|
+
"""
|
|
281
301
|
if not module_name:
|
|
282
302
|
return "external"
|
|
303
|
+
top = module_name.partition(".")[0]
|
|
304
|
+
# Decided by name, not by path: these must never be inlined even when they resolve to a source
|
|
305
|
+
# checkout (a vendored stdlib module, or an editable ``haystack-ai`` install).
|
|
306
|
+
if top in sys.stdlib_module_names:
|
|
307
|
+
return "stdlib"
|
|
308
|
+
if top in _PLATFORM_NAMESPACES:
|
|
309
|
+
return "external"
|
|
283
310
|
try:
|
|
284
311
|
spec = importlib.util.find_spec(module_name)
|
|
285
312
|
except (ImportError, ValueError, ModuleNotFoundError, AttributeError):
|
|
@@ -299,12 +326,19 @@ def _classify_origin(origin_path: Path, project_root: Path) -> str:
|
|
|
299
326
|
virtualenv often lives *inside* the project directory (e.g. ``<project>/.venv/.../site-packages``),
|
|
300
327
|
so installed packages resolve under ``project_root`` yet must be treated as external dependencies,
|
|
301
328
|
not inlined as local source.
|
|
329
|
+
|
|
330
|
+
Anything that is neither installed nor owned by the interpreter is the user's own source, wherever
|
|
331
|
+
it sits on disk. ``project_root`` (the pipeline file's directory) is only a fast positive hint:
|
|
332
|
+
requiring containment in it would miss the standard ``src``-layout repo, where the pipeline lives in
|
|
333
|
+
``pipelines/`` and its components in ``src/``.
|
|
302
334
|
"""
|
|
303
335
|
if _INSTALLED_PACKAGE_DIRS.intersection(origin_path.parts):
|
|
304
336
|
return "external"
|
|
305
337
|
if _is_relative_to(origin_path, project_root):
|
|
306
338
|
return "local"
|
|
307
|
-
|
|
339
|
+
if any(_is_relative_to(origin_path, d) for d in _INTERPRETER_DIRS):
|
|
340
|
+
return "stdlib"
|
|
341
|
+
return "local"
|
|
308
342
|
|
|
309
343
|
|
|
310
344
|
def _is_relative_to(path: Path, root: Path) -> bool:
|
|
@@ -1599,6 +1633,17 @@ def extract_from_pipeline(pipeline: Any, project_root: Path) -> dict:
|
|
|
1599
1633
|
comp_type = comp.get("type", "")
|
|
1600
1634
|
module_name = comp_type.rpartition(".")[0]
|
|
1601
1635
|
if classify_module(module_name, project_root) != "local":
|
|
1636
|
+
if not comp_type.startswith(_PLATFORM_NAMESPACES):
|
|
1637
|
+
# Can't be inlined and isn't something the platform ships. Most likely first-party code
|
|
1638
|
+
# deployed from an installed copy (``pip install ./src``), which we cannot tell apart
|
|
1639
|
+
# from a third-party integration - so say so rather than fail on the platform later.
|
|
1640
|
+
logger.warning(
|
|
1641
|
+
"Component '%s' keeps its original type '%s': it did not resolve to local source, so "
|
|
1642
|
+
"the platform must be able to import it. If it is your own code, deploy from the "
|
|
1643
|
+
"source tree rather than from an installed copy.",
|
|
1644
|
+
comp_name,
|
|
1645
|
+
comp_type,
|
|
1646
|
+
)
|
|
1602
1647
|
continue
|
|
1603
1648
|
logger.debug("Rewriting local component '%s' (%s) to Code", comp_name, comp_type)
|
|
1604
1649
|
code = _build_code_block(comp_type, project_root)
|
{haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/cli.py
RENAMED
|
@@ -27,6 +27,7 @@ from haystack_enterprise_sdk._api.config import (
|
|
|
27
27
|
from haystack_enterprise_sdk._api.deployments import (
|
|
28
28
|
DeploymentMode,
|
|
29
29
|
DeploymentServiceLevel,
|
|
30
|
+
FailedToTagDeploymentError,
|
|
30
31
|
PipelineValidationError,
|
|
31
32
|
)
|
|
32
33
|
from haystack_enterprise_sdk._api.haystack_enterprise_api import HaystackEnterpriseAPIError
|
|
@@ -449,6 +450,9 @@ def deploy( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
449
450
|
memory: Optional[str] = None,
|
|
450
451
|
gpu: Optional[int] = None,
|
|
451
452
|
idle_timeout: Optional[int] = None,
|
|
453
|
+
tag: Optional[List[str]] = typer.Option(
|
|
454
|
+
None, "--tag", help="Tag to add when creating the service (repeatable; up to 3)."
|
|
455
|
+
),
|
|
452
456
|
python: Optional[str] = None,
|
|
453
457
|
dry_run: bool = False,
|
|
454
458
|
output: Optional[Path] = None,
|
|
@@ -492,6 +496,11 @@ def deploy( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
492
496
|
:param memory: Memory limit, e.g. '2Gi' (with --managed).
|
|
493
497
|
:param gpu: GPU memory limit in gigabytes (with --managed).
|
|
494
498
|
:param idle_timeout: Idle timeout in seconds before scale-down (with --managed).
|
|
499
|
+
:param tag: Tag to add to the service when it is created (repeatable; the platform allows at
|
|
500
|
+
most 3 per service). Only applies when the service is created -- pass --create, or omit
|
|
501
|
+
--create against a service name that does not exist yet. Rejected if the service already
|
|
502
|
+
exists, same as --managed and the sizing flags -- use `tag-add`/`tag-remove` to change
|
|
503
|
+
tags on a service after creation.
|
|
495
504
|
:param python: Path to the Python interpreter used to load your pipeline (defaults to an
|
|
496
505
|
auto-detected virtualenv near the target file, else the current interpreter).
|
|
497
506
|
:param dry_run: Transform the pipeline and print/write the resulting YAML without deploying. No
|
|
@@ -524,6 +533,9 @@ def deploy( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
524
533
|
Create a managed service with explicit sizing (activates and waits for the rollout):
|
|
525
534
|
`haystack-enterprise deploy pipeline.py my-service --managed --service-level PRODUCTION --cpu 2`
|
|
526
535
|
|
|
536
|
+
Create a service tagged for a team and a hackathon, up to 3 tags:
|
|
537
|
+
`haystack-enterprise deploy pipeline.py my-service --create --tag team-success --tag hackathon`
|
|
538
|
+
|
|
527
539
|
Describe the revision instead of using the auto-generated comment:
|
|
528
540
|
`haystack-enterprise deploy pipeline.py my-service -m "Bump embedder model to bge-large"`
|
|
529
541
|
|
|
@@ -575,8 +587,8 @@ def deploy( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
575
587
|
if create is False and existing is None:
|
|
576
588
|
typer.echo(f"No service named '{service_name}' in workspace '{workspace_name}'. Drop --no-create to create it.")
|
|
577
589
|
raise typer.Exit(1)
|
|
578
|
-
if existing is not None and (managed or given_sizing):
|
|
579
|
-
creation_flags = (["--managed"] if managed else []) + given_sizing
|
|
590
|
+
if existing is not None and (managed or given_sizing or tag):
|
|
591
|
+
creation_flags = (["--managed"] if managed else []) + given_sizing + (["--tag"] if tag else [])
|
|
580
592
|
applies = "only applies" if len(creation_flags) == 1 else "only apply"
|
|
581
593
|
typer.echo(
|
|
582
594
|
f"Service '{service_name}' already exists; {', '.join(creation_flags)} {applies} when a "
|
|
@@ -596,9 +608,10 @@ def deploy( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
596
608
|
cpu_limit=cpu,
|
|
597
609
|
memory_limit=memory,
|
|
598
610
|
gpu_limit_gigabyte=gpu,
|
|
611
|
+
tags=tuple(tag or ()),
|
|
599
612
|
)
|
|
600
613
|
else:
|
|
601
|
-
create_options = CreateOptions(deployment_mode=DeploymentMode.SERVERLESS)
|
|
614
|
+
create_options = CreateOptions(deployment_mode=DeploymentMode.SERVERLESS, tags=tuple(tag or ()))
|
|
602
615
|
|
|
603
616
|
activate = not skip_activation
|
|
604
617
|
|
|
@@ -1152,8 +1165,8 @@ def _echo_endpoint(client: DeploymentClient, service_name: str, result: DeployRe
|
|
|
1152
1165
|
if not result.is_deployed:
|
|
1153
1166
|
return
|
|
1154
1167
|
|
|
1155
|
-
base_url = client.
|
|
1156
|
-
model = f"{client.workspace_name}/{
|
|
1168
|
+
base_url = client.chat_completions_base_url()
|
|
1169
|
+
model = f"{client.workspace_name}/{result.deployment.deployment_id}"
|
|
1157
1170
|
typer.echo(f"\nChat completions endpoint (OpenAI-compatible):\n POST {base_url}/chat/completions")
|
|
1158
1171
|
typer.echo(
|
|
1159
1172
|
f"\n curl -N {base_url}/chat/completions \\\n"
|
|
@@ -1649,12 +1662,70 @@ def service_status(
|
|
|
1649
1662
|
"pending_revision_id": (
|
|
1650
1663
|
str(deployment.pending_revision_id) if deployment.pending_revision_id else None
|
|
1651
1664
|
),
|
|
1665
|
+
"tags": deployment.tags,
|
|
1652
1666
|
},
|
|
1653
1667
|
indent=4,
|
|
1654
1668
|
)
|
|
1655
1669
|
)
|
|
1656
1670
|
|
|
1657
1671
|
|
|
1672
|
+
@cli_app.command()
|
|
1673
|
+
def tag_add(
|
|
1674
|
+
service_name: str,
|
|
1675
|
+
tag_name: str,
|
|
1676
|
+
api_key: Optional[str] = None,
|
|
1677
|
+
api_url: Optional[str] = None,
|
|
1678
|
+
workspace_name: str = DEFAULT_WORKSPACE_NAME,
|
|
1679
|
+
) -> None:
|
|
1680
|
+
"""Add a tag to a service deployment, independent of deploy/create.
|
|
1681
|
+
|
|
1682
|
+
:param service_name: Name of the service deployment.
|
|
1683
|
+
:param tag_name: Tag name (1-50 chars; letters, digits, spaces, underscores, hyphens).
|
|
1684
|
+
Rejected as a duplicate case-insensitively, and past the third tag on one service.
|
|
1685
|
+
:param api_key: deepset API key to use for authentication.
|
|
1686
|
+
:param api_url: API URL to use for authentication.
|
|
1687
|
+
:param workspace_name: Workspace of the service. Uses the workspace from the .ENV file by default.
|
|
1688
|
+
|
|
1689
|
+
Example:
|
|
1690
|
+
`haystack-enterprise tag-add my-service hackathon`
|
|
1691
|
+
"""
|
|
1692
|
+
client = DeploymentClient(api_key=api_key, api_url=api_url, workspace_name=workspace_name)
|
|
1693
|
+
try:
|
|
1694
|
+
tags = client.add_tag(service_name, tag_name)
|
|
1695
|
+
except (ServiceNotFoundError, FailedToTagDeploymentError) as err:
|
|
1696
|
+
typer.echo(str(err))
|
|
1697
|
+
raise typer.Exit(1) # noqa: B904
|
|
1698
|
+
typer.echo(f"Tags on '{service_name}': {', '.join(tags) or '(none)'}")
|
|
1699
|
+
|
|
1700
|
+
|
|
1701
|
+
@cli_app.command()
|
|
1702
|
+
def tag_remove(
|
|
1703
|
+
service_name: str,
|
|
1704
|
+
tag_name: str,
|
|
1705
|
+
api_key: Optional[str] = None,
|
|
1706
|
+
api_url: Optional[str] = None,
|
|
1707
|
+
workspace_name: str = DEFAULT_WORKSPACE_NAME,
|
|
1708
|
+
) -> None:
|
|
1709
|
+
"""Remove a tag from a service deployment. Matching is case-insensitive.
|
|
1710
|
+
|
|
1711
|
+
:param service_name: Name of the service deployment.
|
|
1712
|
+
:param tag_name: Tag name to remove.
|
|
1713
|
+
:param api_key: deepset API key to use for authentication.
|
|
1714
|
+
:param api_url: API URL to use for authentication.
|
|
1715
|
+
:param workspace_name: Workspace of the service. Uses the workspace from the .ENV file by default.
|
|
1716
|
+
|
|
1717
|
+
Example:
|
|
1718
|
+
`haystack-enterprise tag-remove my-service hackathon`
|
|
1719
|
+
"""
|
|
1720
|
+
client = DeploymentClient(api_key=api_key, api_url=api_url, workspace_name=workspace_name)
|
|
1721
|
+
try:
|
|
1722
|
+
tags = client.remove_tag(service_name, tag_name)
|
|
1723
|
+
except (ServiceNotFoundError, FailedToTagDeploymentError) as err:
|
|
1724
|
+
typer.echo(str(err))
|
|
1725
|
+
raise typer.Exit(1) # noqa: B904
|
|
1726
|
+
typer.echo(f"Tags on '{service_name}': {', '.join(tags) or '(none)'}")
|
|
1727
|
+
|
|
1728
|
+
|
|
1658
1729
|
def version_callback(value: bool) -> None:
|
|
1659
1730
|
"""Show the SDK version and exit.
|
|
1660
1731
|
|
|
File without changes
|
|
@@ -19,6 +19,7 @@ from haystack_enterprise_sdk._api.deployments import (
|
|
|
19
19
|
)
|
|
20
20
|
from haystack_enterprise_sdk._api.haystack_enterprise_api import (
|
|
21
21
|
HaystackEnterpriseAPI,
|
|
22
|
+
chat_completions_base_url,
|
|
22
23
|
deployment_base_url,
|
|
23
24
|
)
|
|
24
25
|
from haystack_enterprise_sdk._api.pipeline_run import DEFAULT_RUN_RETRIES, OnRetry
|
|
@@ -76,16 +77,23 @@ class AsyncDeploymentClient:
|
|
|
76
77
|
return self._workspace_name
|
|
77
78
|
|
|
78
79
|
def deployment_base_url(self, deployment_id: Any) -> str:
|
|
79
|
-
"""The
|
|
80
|
-
|
|
81
|
-
Append ``/chat/completions`` to call it directly, or hand it to an OpenAI client as ``base_url``.
|
|
82
|
-
Only usable once the deployment has an active revision.
|
|
80
|
+
"""The per-deployment base URL (for per-deployment routes like ``/chat`` and ``/chat-stream``).
|
|
83
81
|
|
|
84
82
|
:param deployment_id: Id of the deployment (e.g. ``DeployResult.deployment.deployment_id``).
|
|
85
83
|
:return: The deployment's base URL.
|
|
86
84
|
"""
|
|
87
85
|
return deployment_base_url(self._api_config.api_url, self._workspace_name, deployment_id)
|
|
88
86
|
|
|
87
|
+
def chat_completions_base_url(self) -> str:
|
|
88
|
+
"""The workspace-scoped OpenAI-compatible base URL for chat completions.
|
|
89
|
+
|
|
90
|
+
Use ``POST <this>/chat/completions`` with ``model`` set to ``{workspace}/{deployment_id}`` (UUID).
|
|
91
|
+
Pass this as ``base_url`` to an OpenAI client.
|
|
92
|
+
|
|
93
|
+
:return: e.g. ``https://api.cloud.deepset.ai/api/v1/workspaces/my-ws/deployments/v1``.
|
|
94
|
+
"""
|
|
95
|
+
return chat_completions_base_url(self._api_config.api_url, self._workspace_name)
|
|
96
|
+
|
|
89
97
|
@asynccontextmanager
|
|
90
98
|
async def _service(self) -> AsyncIterator[DeploymentService]:
|
|
91
99
|
"""Yield a :class:`DeploymentService` backed by a managed API client."""
|
|
@@ -210,6 +218,20 @@ class AsyncDeploymentClient:
|
|
|
210
218
|
async with self._service() as service:
|
|
211
219
|
return await service.get_service_status(service_name)
|
|
212
220
|
|
|
221
|
+
async def add_tag(self, service_name: str, tag_name: str) -> List[str]:
|
|
222
|
+
"""Add a tag to a service by name, independent of deploy/create. See
|
|
223
|
+
:meth:`haystack_enterprise_sdk._service.deployment_service.DeploymentService.add_tag`.
|
|
224
|
+
"""
|
|
225
|
+
async with self._service() as service:
|
|
226
|
+
return await service.add_tag(service_name, tag_name)
|
|
227
|
+
|
|
228
|
+
async def remove_tag(self, service_name: str, tag_name: str) -> List[str]:
|
|
229
|
+
"""Remove a tag from a service by name. See
|
|
230
|
+
:meth:`haystack_enterprise_sdk._service.deployment_service.DeploymentService.remove_tag`.
|
|
231
|
+
"""
|
|
232
|
+
async with self._service() as service:
|
|
233
|
+
return await service.remove_tag(service_name, tag_name)
|
|
234
|
+
|
|
213
235
|
async def create_shared_prototype(
|
|
214
236
|
self, service_name: str, options: Optional[ShareOptions] = None
|
|
215
237
|
) -> SharedPrototype:
|
|
@@ -183,6 +183,14 @@ class DeploymentClient: # pylint: disable=too-few-public-methods
|
|
|
183
183
|
"""Return the current deployment (with live runtime status) for ``service_name``."""
|
|
184
184
|
return _run(self._async_client.get_service_status(service_name))
|
|
185
185
|
|
|
186
|
+
def add_tag(self, service_name: str, tag_name: str) -> List[str]:
|
|
187
|
+
"""Add a tag to a service by name, independent of deploy/create, synchronously."""
|
|
188
|
+
return _run(self._async_client.add_tag(service_name, tag_name))
|
|
189
|
+
|
|
190
|
+
def remove_tag(self, service_name: str, tag_name: str) -> List[str]:
|
|
191
|
+
"""Remove a tag from a service by name synchronously."""
|
|
192
|
+
return _run(self._async_client.remove_tag(service_name, tag_name))
|
|
193
|
+
|
|
186
194
|
def create_shared_prototype(self, service_name: str, options: Optional[ShareOptions] = None) -> SharedPrototype:
|
|
187
195
|
"""Create a shared prototype (a shareable chat UI link) for a deployed service synchronously."""
|
|
188
196
|
return _run(self._async_client.create_shared_prototype(service_name, options))
|
|
@@ -193,5 +201,9 @@ class DeploymentClient: # pylint: disable=too-few-public-methods
|
|
|
193
201
|
return self._async_client.workspace_name
|
|
194
202
|
|
|
195
203
|
def deployment_base_url(self, deployment_id: Any) -> str:
|
|
196
|
-
"""The
|
|
204
|
+
"""The per-deployment base URL. See :meth:`AsyncDeploymentClient.deployment_base_url`."""
|
|
197
205
|
return self._async_client.deployment_base_url(deployment_id)
|
|
206
|
+
|
|
207
|
+
def chat_completions_base_url(self) -> str:
|
|
208
|
+
"""The workspace-scoped chat-completions base URL. See :meth:`AsyncDeploymentClient.chat_completions_base_url`."""
|
|
209
|
+
return self._async_client.chat_completions_base_url()
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["uv_build>=0.9.13,<0.
|
|
2
|
+
requires = ["uv_build>=0.9.13,<0.13.0"]
|
|
3
3
|
build-backend = "uv_build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "haystack-enterprise-sdk"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Haystack Enterprise
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Python SDK and CLI for the Haystack Enterprise Platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
|
-
keywords = [
|
|
13
|
+
keywords = [
|
|
14
|
+
"agents",
|
|
15
|
+
"deepset",
|
|
16
|
+
"haystack",
|
|
17
|
+
"llm",
|
|
18
|
+
"pipelines",
|
|
19
|
+
"rag",
|
|
20
|
+
"sdk",
|
|
21
|
+
]
|
|
14
22
|
classifiers = [
|
|
15
23
|
"Development Status :: 4 - Beta",
|
|
24
|
+
"Environment :: Console",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
16
27
|
"Programming Language :: Python",
|
|
17
28
|
"Programming Language :: Python :: 3",
|
|
18
29
|
"Programming Language :: Python :: 3.10",
|
|
@@ -20,6 +31,8 @@ classifiers = [
|
|
|
20
31
|
"Programming Language :: Python :: 3.12",
|
|
21
32
|
"Programming Language :: Python :: 3.13",
|
|
22
33
|
"Programming Language :: Python :: 3.14",
|
|
34
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
35
|
+
"Typing :: Typed",
|
|
23
36
|
]
|
|
24
37
|
dependencies = [
|
|
25
38
|
"structlog>=24.0.0",
|
|
@@ -66,7 +79,7 @@ code-quality = [
|
|
|
66
79
|
"ruff==0.15.12",
|
|
67
80
|
"mypy==1.19.1",
|
|
68
81
|
"pre-commit==4.3.0",
|
|
69
|
-
"types-aiofiles==25.1.0.
|
|
82
|
+
"types-aiofiles==25.1.0.20260518",
|
|
70
83
|
"types-tabulate==0.9.0.20241207",
|
|
71
84
|
]
|
|
72
85
|
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["uv_build>=0.9.13,<0.
|
|
2
|
+
requires = ["uv_build>=0.9.13,<0.13.0"]
|
|
3
3
|
build-backend = "uv_build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "haystack-enterprise-sdk"
|
|
7
|
-
version = "0.
|
|
8
|
-
description =
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Python SDK and CLI for the Haystack Enterprise Platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
|
-
keywords = [
|
|
13
|
+
keywords = [
|
|
14
|
+
"agents",
|
|
15
|
+
"deepset",
|
|
16
|
+
"haystack",
|
|
17
|
+
"llm",
|
|
18
|
+
"pipelines",
|
|
19
|
+
"rag",
|
|
20
|
+
"sdk",
|
|
21
|
+
]
|
|
14
22
|
authors = [{ name = "deepset.ai", email = "malte.pietsch@deepset.ai" }]
|
|
15
23
|
classifiers = [
|
|
16
24
|
"Development Status :: 4 - Beta",
|
|
25
|
+
"Environment :: Console",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
17
28
|
"Programming Language :: Python",
|
|
18
29
|
"Programming Language :: Python :: 3",
|
|
19
30
|
"Programming Language :: Python :: 3.10",
|
|
@@ -21,6 +32,8 @@ classifiers = [
|
|
|
21
32
|
"Programming Language :: Python :: 3.12",
|
|
22
33
|
"Programming Language :: Python :: 3.13",
|
|
23
34
|
"Programming Language :: Python :: 3.14",
|
|
35
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
36
|
+
"Typing :: Typed",
|
|
24
37
|
]
|
|
25
38
|
dependencies = [
|
|
26
39
|
"structlog>=24.0.0",
|
|
@@ -70,7 +83,7 @@ code-quality = [
|
|
|
70
83
|
"ruff==0.15.12",
|
|
71
84
|
"mypy==1.19.1",
|
|
72
85
|
"pre-commit==4.3.0",
|
|
73
|
-
"types-aiofiles==25.1.0.
|
|
86
|
+
"types-aiofiles==25.1.0.20260518",
|
|
74
87
|
"types-tabulate==0.9.0.20241207",
|
|
75
88
|
]
|
|
76
89
|
|
|
File without changes
|
{haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/_console.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{haystack_enterprise_sdk-0.1.0 → haystack_enterprise_sdk-0.2.0}/haystack_enterprise_sdk/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|