langgraph-api 0.4.40__py3-none-any.whl → 0.5.6__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.
Potentially problematic release.
This version of langgraph-api might be problematic. Click here for more details.
- langgraph_api/__init__.py +1 -1
- langgraph_api/api/assistants.py +65 -61
- langgraph_api/api/meta.py +6 -0
- langgraph_api/api/threads.py +11 -7
- langgraph_api/auth/custom.py +29 -24
- langgraph_api/cli.py +2 -49
- langgraph_api/config.py +131 -16
- langgraph_api/graph.py +1 -1
- langgraph_api/grpc/client.py +183 -0
- langgraph_api/grpc/config_conversion.py +225 -0
- langgraph_api/grpc/generated/core_api_pb2.py +275 -0
- langgraph_api/{grpc_ops → grpc}/generated/core_api_pb2.pyi +35 -40
- langgraph_api/grpc/generated/engine_common_pb2.py +190 -0
- langgraph_api/grpc/generated/engine_common_pb2.pyi +634 -0
- langgraph_api/grpc/generated/engine_common_pb2_grpc.py +24 -0
- langgraph_api/grpc/ops.py +1045 -0
- langgraph_api/js/build.mts +1 -1
- langgraph_api/js/client.http.mts +1 -1
- langgraph_api/js/client.mts +1 -1
- langgraph_api/js/package.json +12 -12
- langgraph_api/js/src/graph.mts +20 -0
- langgraph_api/js/yarn.lock +176 -234
- langgraph_api/metadata.py +29 -21
- langgraph_api/queue_entrypoint.py +2 -2
- langgraph_api/route.py +14 -4
- langgraph_api/schema.py +2 -2
- langgraph_api/self_hosted_metrics.py +48 -2
- langgraph_api/serde.py +58 -14
- langgraph_api/server.py +16 -2
- langgraph_api/worker.py +1 -1
- {langgraph_api-0.4.40.dist-info → langgraph_api-0.5.6.dist-info}/METADATA +6 -6
- {langgraph_api-0.4.40.dist-info → langgraph_api-0.5.6.dist-info}/RECORD +38 -34
- langgraph_api/grpc_ops/client.py +0 -80
- langgraph_api/grpc_ops/generated/core_api_pb2.py +0 -274
- langgraph_api/grpc_ops/ops.py +0 -610
- /langgraph_api/{grpc_ops → grpc}/__init__.py +0 -0
- /langgraph_api/{grpc_ops → grpc}/generated/__init__.py +0 -0
- /langgraph_api/{grpc_ops → grpc}/generated/core_api_pb2_grpc.py +0 -0
- {langgraph_api-0.4.40.dist-info → langgraph_api-0.5.6.dist-info}/WHEEL +0 -0
- {langgraph_api-0.4.40.dist-info → langgraph_api-0.5.6.dist-info}/entry_points.txt +0 -0
- {langgraph_api-0.4.40.dist-info → langgraph_api-0.5.6.dist-info}/licenses/LICENSE +0 -0
langgraph_api/grpc_ops/ops.py
DELETED
|
@@ -1,610 +0,0 @@
|
|
|
1
|
-
"""gRPC-based operations for LangGraph API."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import asyncio
|
|
6
|
-
import functools
|
|
7
|
-
from collections.abc import AsyncIterator
|
|
8
|
-
from datetime import UTC
|
|
9
|
-
from http import HTTPStatus
|
|
10
|
-
from typing import Any
|
|
11
|
-
from uuid import UUID
|
|
12
|
-
|
|
13
|
-
import orjson
|
|
14
|
-
import structlog
|
|
15
|
-
from google.protobuf.json_format import MessageToDict
|
|
16
|
-
from google.protobuf.struct_pb2 import Struct # type: ignore[import]
|
|
17
|
-
from grpc import StatusCode
|
|
18
|
-
from grpc.aio import AioRpcError
|
|
19
|
-
from langgraph_sdk.schema import Config
|
|
20
|
-
from starlette.exceptions import HTTPException
|
|
21
|
-
|
|
22
|
-
from langgraph_api.schema import (
|
|
23
|
-
Assistant,
|
|
24
|
-
AssistantSelectField,
|
|
25
|
-
Context,
|
|
26
|
-
MetadataInput,
|
|
27
|
-
OnConflictBehavior,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
from .client import GrpcClient
|
|
31
|
-
from .generated import core_api_pb2 as pb
|
|
32
|
-
|
|
33
|
-
GRPC_STATUS_TO_HTTP_STATUS = {
|
|
34
|
-
StatusCode.NOT_FOUND: HTTPStatus.NOT_FOUND,
|
|
35
|
-
StatusCode.ALREADY_EXISTS: HTTPStatus.CONFLICT,
|
|
36
|
-
StatusCode.INVALID_ARGUMENT: HTTPStatus.UNPROCESSABLE_ENTITY,
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
logger = structlog.stdlib.get_logger(__name__)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def map_if_exists(if_exists: str) -> pb.OnConflictBehavior:
|
|
43
|
-
if if_exists == "do_nothing":
|
|
44
|
-
return pb.OnConflictBehavior.DO_NOTHING
|
|
45
|
-
return pb.OnConflictBehavior.RAISE
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def map_configurable(config: Config) -> Struct:
|
|
49
|
-
"""Build pb.Config, placing non-standard keys into `extra` bytes.
|
|
50
|
-
|
|
51
|
-
The `extra` field mirrors any keys that are not first-class in
|
|
52
|
-
Config (e.g., "tags", "recursion_limit", "configurable").
|
|
53
|
-
It is JSON-encoded bytes to minimize serde overhead; the server will
|
|
54
|
-
unpack and persist them as top-level keys.
|
|
55
|
-
"""
|
|
56
|
-
base_keys = {"tags", "recursion_limit", "configurable"}
|
|
57
|
-
extra_dict = {k: v for k, v in (config or {}).items() if k not in base_keys}
|
|
58
|
-
|
|
59
|
-
kwargs: dict[str, Any] = dict(
|
|
60
|
-
tags=config.get("tags"),
|
|
61
|
-
recursion_limit=config.get("recursion_limit"),
|
|
62
|
-
configurable=(
|
|
63
|
-
dict_to_struct(config.get("configurable", {}))
|
|
64
|
-
if config.get("configurable")
|
|
65
|
-
else None
|
|
66
|
-
),
|
|
67
|
-
)
|
|
68
|
-
if extra_dict:
|
|
69
|
-
kwargs["extra"] = orjson.dumps(extra_dict)
|
|
70
|
-
|
|
71
|
-
return pb.Config(**kwargs)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def consolidate_config_and_context(
|
|
75
|
-
config: Config | None, context: Context | None
|
|
76
|
-
) -> tuple[Config, Context | None]:
|
|
77
|
-
"""Return a new (config, context) with consistent configurable/context.
|
|
78
|
-
|
|
79
|
-
Does not mutate the passed-in objects. If both configurable and context
|
|
80
|
-
are provided, raises 400. If only one is provided, mirrors it to the other.
|
|
81
|
-
"""
|
|
82
|
-
cfg: Config = dict(config or {})
|
|
83
|
-
ctx: Context | None = dict(context) if context is not None else None
|
|
84
|
-
|
|
85
|
-
if cfg.get("configurable") and ctx:
|
|
86
|
-
raise HTTPException(
|
|
87
|
-
status_code=400,
|
|
88
|
-
detail="Cannot specify both configurable and context. Prefer setting context alone. Context was introduced in LangGraph 0.6.0 and is the long term planned replacement for configurable.",
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
if cfg.get("configurable"):
|
|
92
|
-
ctx = cfg["configurable"]
|
|
93
|
-
elif ctx is not None:
|
|
94
|
-
cfg["configurable"] = ctx
|
|
95
|
-
|
|
96
|
-
return cfg, ctx
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def dict_to_struct(data: dict[str, Any]) -> Struct:
|
|
100
|
-
"""Convert a dictionary to a protobuf Struct."""
|
|
101
|
-
struct = Struct()
|
|
102
|
-
if data:
|
|
103
|
-
struct.update(data)
|
|
104
|
-
return struct
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def struct_to_dict(struct: Struct) -> dict[str, Any]:
|
|
108
|
-
"""Convert a protobuf Struct to a dictionary."""
|
|
109
|
-
return MessageToDict(struct) if struct else {}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def _runnable_config_to_user_dict(cfg: pb.Config | None) -> dict[str, Any]:
|
|
113
|
-
"""Convert pb.Config to user-visible dict, unpacking `extra`.
|
|
114
|
-
|
|
115
|
-
- Keeps top-level known keys: tags, recursion_limit, configurable.
|
|
116
|
-
- Merges keys from `extra` into the top-level dict.
|
|
117
|
-
"""
|
|
118
|
-
if not cfg:
|
|
119
|
-
return {}
|
|
120
|
-
|
|
121
|
-
out: dict[str, Any] = {}
|
|
122
|
-
# tags
|
|
123
|
-
if cfg.tags:
|
|
124
|
-
out["tags"] = list(cfg.tags)
|
|
125
|
-
# recursion_limit (preserve presence of 0 if set)
|
|
126
|
-
try:
|
|
127
|
-
if cfg.HasField("recursion_limit"):
|
|
128
|
-
out["recursion_limit"] = cfg.recursion_limit
|
|
129
|
-
except ValueError:
|
|
130
|
-
# Some runtimes may not support HasField on certain builds; fallback
|
|
131
|
-
if getattr(cfg, "recursion_limit", None) is not None:
|
|
132
|
-
out["recursion_limit"] = cfg.recursion_limit
|
|
133
|
-
# configurable
|
|
134
|
-
if cfg.HasField("configurable"):
|
|
135
|
-
out["configurable"] = struct_to_dict(cfg.configurable)
|
|
136
|
-
# extra (bytes: JSON-encoded object)
|
|
137
|
-
if cfg.HasField("extra") and cfg.extra:
|
|
138
|
-
extra = orjson.loads(cfg.extra)
|
|
139
|
-
if isinstance(extra, dict) and extra:
|
|
140
|
-
out.update(extra)
|
|
141
|
-
|
|
142
|
-
return out
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def proto_to_assistant(proto_assistant: pb.Assistant) -> Assistant:
|
|
146
|
-
"""Convert protobuf Assistant to dictionary format."""
|
|
147
|
-
# Preserve None for optional scalar fields by checking presence via HasField
|
|
148
|
-
description = (
|
|
149
|
-
proto_assistant.description if proto_assistant.HasField("description") else None
|
|
150
|
-
)
|
|
151
|
-
return {
|
|
152
|
-
"assistant_id": proto_assistant.assistant_id,
|
|
153
|
-
"graph_id": proto_assistant.graph_id,
|
|
154
|
-
"version": proto_assistant.version,
|
|
155
|
-
"created_at": proto_assistant.created_at.ToDatetime(tzinfo=UTC),
|
|
156
|
-
"updated_at": proto_assistant.updated_at.ToDatetime(tzinfo=UTC),
|
|
157
|
-
"config": _runnable_config_to_user_dict(proto_assistant.config),
|
|
158
|
-
"context": struct_to_dict(proto_assistant.context),
|
|
159
|
-
"metadata": struct_to_dict(proto_assistant.metadata),
|
|
160
|
-
"name": proto_assistant.name,
|
|
161
|
-
"description": description,
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def _map_sort_by(sort_by: str | None) -> pb.AssistantsSortBy:
|
|
166
|
-
"""Map string sort_by to protobuf enum."""
|
|
167
|
-
if not sort_by:
|
|
168
|
-
return pb.AssistantsSortBy.CREATED_AT
|
|
169
|
-
|
|
170
|
-
sort_by_lower = sort_by.lower()
|
|
171
|
-
mapping = {
|
|
172
|
-
"assistant_id": pb.AssistantsSortBy.ASSISTANT_ID,
|
|
173
|
-
"graph_id": pb.AssistantsSortBy.GRAPH_ID,
|
|
174
|
-
"name": pb.AssistantsSortBy.NAME,
|
|
175
|
-
"created_at": pb.AssistantsSortBy.CREATED_AT,
|
|
176
|
-
"updated_at": pb.AssistantsSortBy.UPDATED_AT,
|
|
177
|
-
}
|
|
178
|
-
return mapping.get(sort_by_lower, pb.AssistantsSortBy.CREATED_AT)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def _map_sort_order(sort_order: str | None) -> pb.SortOrder:
|
|
182
|
-
"""Map string sort_order to protobuf enum."""
|
|
183
|
-
if sort_order and sort_order.upper() == "ASC":
|
|
184
|
-
return pb.SortOrder.ASC
|
|
185
|
-
return pb.SortOrder.DESC
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def _handle_grpc_error(error: AioRpcError) -> None:
|
|
189
|
-
"""Handle gRPC errors and convert to appropriate exceptions."""
|
|
190
|
-
raise HTTPException(
|
|
191
|
-
status_code=GRPC_STATUS_TO_HTTP_STATUS.get(
|
|
192
|
-
error.code(), HTTPStatus.INTERNAL_SERVER_ERROR
|
|
193
|
-
),
|
|
194
|
-
detail=str(error.details()),
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
class Authenticated:
|
|
199
|
-
"""Base class for authenticated operations (matches storage_postgres interface)."""
|
|
200
|
-
|
|
201
|
-
resource: str = "assistants"
|
|
202
|
-
|
|
203
|
-
@classmethod
|
|
204
|
-
async def handle_event(
|
|
205
|
-
cls,
|
|
206
|
-
ctx: Any, # Auth context
|
|
207
|
-
action: str,
|
|
208
|
-
value: Any,
|
|
209
|
-
) -> dict[str, Any] | None:
|
|
210
|
-
"""Handle authentication event - stub implementation for now."""
|
|
211
|
-
# TODO: Implement proper auth handling that converts auth context
|
|
212
|
-
# to gRPC AuthFilter format when needed
|
|
213
|
-
return None
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def grpc_error_guard(cls):
|
|
217
|
-
"""Class decorator to wrap async methods and handle gRPC errors uniformly."""
|
|
218
|
-
for name, attr in list(cls.__dict__.items()):
|
|
219
|
-
func = None
|
|
220
|
-
wrapper_type = None
|
|
221
|
-
if isinstance(attr, staticmethod):
|
|
222
|
-
func = attr.__func__
|
|
223
|
-
wrapper_type = staticmethod
|
|
224
|
-
elif isinstance(attr, classmethod):
|
|
225
|
-
func = attr.__func__
|
|
226
|
-
wrapper_type = classmethod
|
|
227
|
-
elif callable(attr):
|
|
228
|
-
func = attr
|
|
229
|
-
|
|
230
|
-
if func and asyncio.iscoroutinefunction(func):
|
|
231
|
-
|
|
232
|
-
def make_wrapper(f):
|
|
233
|
-
@functools.wraps(f)
|
|
234
|
-
async def wrapped(*args, **kwargs):
|
|
235
|
-
try:
|
|
236
|
-
return await f(*args, **kwargs)
|
|
237
|
-
except AioRpcError as e:
|
|
238
|
-
_handle_grpc_error(e)
|
|
239
|
-
|
|
240
|
-
return wrapped # noqa: B023
|
|
241
|
-
|
|
242
|
-
wrapped = make_wrapper(func)
|
|
243
|
-
if wrapper_type is staticmethod:
|
|
244
|
-
setattr(cls, name, staticmethod(wrapped))
|
|
245
|
-
elif wrapper_type is classmethod:
|
|
246
|
-
setattr(cls, name, classmethod(wrapped))
|
|
247
|
-
else:
|
|
248
|
-
setattr(cls, name, wrapped)
|
|
249
|
-
return cls
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
@grpc_error_guard
|
|
253
|
-
class Assistants(Authenticated):
|
|
254
|
-
"""gRPC-based assistants operations."""
|
|
255
|
-
|
|
256
|
-
resource = "assistants"
|
|
257
|
-
|
|
258
|
-
@staticmethod
|
|
259
|
-
async def search(
|
|
260
|
-
conn, # Not used in gRPC implementation
|
|
261
|
-
*,
|
|
262
|
-
graph_id: str | None,
|
|
263
|
-
metadata: MetadataInput,
|
|
264
|
-
limit: int,
|
|
265
|
-
offset: int,
|
|
266
|
-
sort_by: str | None = None,
|
|
267
|
-
sort_order: str | None = None,
|
|
268
|
-
select: list[AssistantSelectField] | None = None,
|
|
269
|
-
ctx: Any = None,
|
|
270
|
-
) -> tuple[AsyncIterator[Assistant], int | None]: # type: ignore[return-value]
|
|
271
|
-
"""Search assistants via gRPC."""
|
|
272
|
-
# Handle auth filters
|
|
273
|
-
auth_filters = await Assistants.handle_event(
|
|
274
|
-
ctx,
|
|
275
|
-
"search",
|
|
276
|
-
{
|
|
277
|
-
"graph_id": graph_id,
|
|
278
|
-
"metadata": metadata,
|
|
279
|
-
"limit": limit,
|
|
280
|
-
"offset": offset,
|
|
281
|
-
},
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
# Build the gRPC request
|
|
285
|
-
request = pb.SearchAssistantsRequest(
|
|
286
|
-
filters=auth_filters,
|
|
287
|
-
graph_id=graph_id,
|
|
288
|
-
metadata=dict_to_struct(metadata or {}),
|
|
289
|
-
limit=limit,
|
|
290
|
-
offset=offset,
|
|
291
|
-
sort_by=_map_sort_by(sort_by),
|
|
292
|
-
sort_order=_map_sort_order(sort_order),
|
|
293
|
-
select=select,
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
# Make the gRPC call
|
|
297
|
-
async with GrpcClient() as client:
|
|
298
|
-
response = await client.assistants.Search(request)
|
|
299
|
-
|
|
300
|
-
# Convert response to expected format
|
|
301
|
-
assistants = [
|
|
302
|
-
proto_to_assistant(assistant) for assistant in response.assistants
|
|
303
|
-
]
|
|
304
|
-
|
|
305
|
-
# Determine if there are more results
|
|
306
|
-
# Note: gRPC doesn't return cursor info, so we estimate based on result count
|
|
307
|
-
cursor = offset + limit if len(assistants) == limit else None
|
|
308
|
-
|
|
309
|
-
async def generate_results():
|
|
310
|
-
for assistant in assistants:
|
|
311
|
-
yield {
|
|
312
|
-
k: v for k, v in assistant.items() if select is None or k in select
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return generate_results(), cursor
|
|
316
|
-
|
|
317
|
-
@staticmethod
|
|
318
|
-
async def get(
|
|
319
|
-
conn, # Not used in gRPC implementation
|
|
320
|
-
assistant_id: UUID | str,
|
|
321
|
-
ctx: Any = None,
|
|
322
|
-
) -> AsyncIterator[Assistant]: # type: ignore[return-value]
|
|
323
|
-
"""Get assistant by ID via gRPC."""
|
|
324
|
-
# Handle auth filters
|
|
325
|
-
auth_filters = await Assistants.handle_event(
|
|
326
|
-
ctx, "read", {"assistant_id": str(assistant_id)}
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
# Build the gRPC request
|
|
330
|
-
request = pb.GetAssistantRequest(
|
|
331
|
-
assistant_id=str(assistant_id),
|
|
332
|
-
filters=auth_filters or {},
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
# Make the gRPC call
|
|
336
|
-
async with GrpcClient() as client:
|
|
337
|
-
response = await client.assistants.Get(request)
|
|
338
|
-
|
|
339
|
-
# Convert and yield the result
|
|
340
|
-
assistant = proto_to_assistant(response)
|
|
341
|
-
|
|
342
|
-
async def generate_result():
|
|
343
|
-
yield assistant
|
|
344
|
-
|
|
345
|
-
return generate_result()
|
|
346
|
-
|
|
347
|
-
@staticmethod
|
|
348
|
-
async def put(
|
|
349
|
-
conn, # Not used in gRPC implementation
|
|
350
|
-
assistant_id: UUID | str,
|
|
351
|
-
*,
|
|
352
|
-
graph_id: str,
|
|
353
|
-
config: Config,
|
|
354
|
-
context: Context,
|
|
355
|
-
metadata: MetadataInput,
|
|
356
|
-
if_exists: OnConflictBehavior,
|
|
357
|
-
name: str,
|
|
358
|
-
description: str | None = None,
|
|
359
|
-
ctx: Any = None,
|
|
360
|
-
) -> AsyncIterator[Assistant]: # type: ignore[return-value]
|
|
361
|
-
"""Create/update assistant via gRPC."""
|
|
362
|
-
# Handle auth filters
|
|
363
|
-
auth_filters = await Assistants.handle_event(
|
|
364
|
-
ctx,
|
|
365
|
-
"create",
|
|
366
|
-
{
|
|
367
|
-
"assistant_id": str(assistant_id),
|
|
368
|
-
"graph_id": graph_id,
|
|
369
|
-
"config": config,
|
|
370
|
-
"context": context,
|
|
371
|
-
"metadata": metadata,
|
|
372
|
-
"name": name,
|
|
373
|
-
"description": description,
|
|
374
|
-
},
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
config, context = consolidate_config_and_context(config, context)
|
|
378
|
-
|
|
379
|
-
on_conflict = map_if_exists(if_exists)
|
|
380
|
-
|
|
381
|
-
# Build the gRPC request
|
|
382
|
-
request = pb.CreateAssistantRequest(
|
|
383
|
-
assistant_id=str(assistant_id),
|
|
384
|
-
graph_id=graph_id,
|
|
385
|
-
filters=auth_filters or {},
|
|
386
|
-
if_exists=on_conflict,
|
|
387
|
-
config=map_configurable(config),
|
|
388
|
-
context=dict_to_struct(context or {}),
|
|
389
|
-
name=name,
|
|
390
|
-
description=description,
|
|
391
|
-
metadata=dict_to_struct(metadata or {}),
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
# Make the gRPC call
|
|
395
|
-
async with GrpcClient() as client:
|
|
396
|
-
response = await client.assistants.Create(request)
|
|
397
|
-
|
|
398
|
-
# Convert and yield the result
|
|
399
|
-
assistant = proto_to_assistant(response)
|
|
400
|
-
|
|
401
|
-
async def generate_result():
|
|
402
|
-
yield assistant
|
|
403
|
-
|
|
404
|
-
return generate_result()
|
|
405
|
-
|
|
406
|
-
@staticmethod
|
|
407
|
-
async def patch(
|
|
408
|
-
conn, # Not used in gRPC implementation
|
|
409
|
-
assistant_id: UUID | str,
|
|
410
|
-
*,
|
|
411
|
-
config: dict | None = None,
|
|
412
|
-
context: Context | None = None,
|
|
413
|
-
graph_id: str | None = None,
|
|
414
|
-
metadata: MetadataInput | None = None,
|
|
415
|
-
name: str | None = None,
|
|
416
|
-
description: str | None = None,
|
|
417
|
-
ctx: Any = None,
|
|
418
|
-
) -> AsyncIterator[Assistant]: # type: ignore[return-value]
|
|
419
|
-
"""Update assistant via gRPC."""
|
|
420
|
-
metadata = metadata if metadata is not None else {}
|
|
421
|
-
config = config if config is not None else {}
|
|
422
|
-
# Handle auth filters
|
|
423
|
-
auth_filters = await Assistants.handle_event(
|
|
424
|
-
ctx,
|
|
425
|
-
"update",
|
|
426
|
-
{
|
|
427
|
-
"assistant_id": str(assistant_id),
|
|
428
|
-
"graph_id": graph_id,
|
|
429
|
-
"config": config,
|
|
430
|
-
"context": context,
|
|
431
|
-
"metadata": metadata,
|
|
432
|
-
"name": name,
|
|
433
|
-
"description": description,
|
|
434
|
-
},
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
config, context = consolidate_config_and_context(config, context)
|
|
438
|
-
|
|
439
|
-
# Build the gRPC request
|
|
440
|
-
request = pb.PatchAssistantRequest(
|
|
441
|
-
assistant_id=str(assistant_id),
|
|
442
|
-
filters=auth_filters or {},
|
|
443
|
-
graph_id=graph_id,
|
|
444
|
-
name=name,
|
|
445
|
-
description=description,
|
|
446
|
-
metadata=dict_to_struct(metadata or {}),
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
# Add optional config if provided
|
|
450
|
-
if config:
|
|
451
|
-
request.config.CopyFrom(map_configurable(config))
|
|
452
|
-
|
|
453
|
-
# Add optional context if provided
|
|
454
|
-
if context:
|
|
455
|
-
request.context.CopyFrom(dict_to_struct(context))
|
|
456
|
-
|
|
457
|
-
# Make the gRPC call
|
|
458
|
-
async with GrpcClient() as client:
|
|
459
|
-
response = await client.assistants.Patch(request)
|
|
460
|
-
|
|
461
|
-
# Convert and yield the result
|
|
462
|
-
assistant = proto_to_assistant(response)
|
|
463
|
-
|
|
464
|
-
async def generate_result():
|
|
465
|
-
yield assistant
|
|
466
|
-
|
|
467
|
-
return generate_result()
|
|
468
|
-
|
|
469
|
-
@staticmethod
|
|
470
|
-
async def delete(
|
|
471
|
-
conn, # Not used in gRPC implementation
|
|
472
|
-
assistant_id: UUID | str,
|
|
473
|
-
ctx: Any = None,
|
|
474
|
-
) -> AsyncIterator[UUID]: # type: ignore[return-value]
|
|
475
|
-
"""Delete assistant via gRPC."""
|
|
476
|
-
# Handle auth filters
|
|
477
|
-
auth_filters = await Assistants.handle_event(
|
|
478
|
-
ctx, "delete", {"assistant_id": str(assistant_id)}
|
|
479
|
-
)
|
|
480
|
-
|
|
481
|
-
# Build the gRPC request
|
|
482
|
-
request = pb.DeleteAssistantRequest(
|
|
483
|
-
assistant_id=str(assistant_id),
|
|
484
|
-
filters=auth_filters or {},
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
# Make the gRPC call
|
|
488
|
-
async with GrpcClient() as client:
|
|
489
|
-
await client.assistants.Delete(request)
|
|
490
|
-
|
|
491
|
-
# Return the deleted ID
|
|
492
|
-
async def generate_result():
|
|
493
|
-
yield UUID(str(assistant_id))
|
|
494
|
-
|
|
495
|
-
return generate_result()
|
|
496
|
-
|
|
497
|
-
@staticmethod
|
|
498
|
-
async def set_latest(
|
|
499
|
-
conn, # Not used in gRPC implementation
|
|
500
|
-
assistant_id: UUID | str,
|
|
501
|
-
version: int,
|
|
502
|
-
ctx: Any = None,
|
|
503
|
-
) -> AsyncIterator[Assistant]: # type: ignore[return-value]
|
|
504
|
-
"""Set latest version of assistant via gRPC."""
|
|
505
|
-
# Handle auth filters
|
|
506
|
-
auth_filters = await Assistants.handle_event(
|
|
507
|
-
ctx,
|
|
508
|
-
"update",
|
|
509
|
-
{
|
|
510
|
-
"assistant_id": str(assistant_id),
|
|
511
|
-
"version": version,
|
|
512
|
-
},
|
|
513
|
-
)
|
|
514
|
-
|
|
515
|
-
# Build the gRPC request
|
|
516
|
-
request = pb.SetLatestAssistantRequest(
|
|
517
|
-
assistant_id=str(assistant_id),
|
|
518
|
-
version=version,
|
|
519
|
-
filters=auth_filters or {},
|
|
520
|
-
)
|
|
521
|
-
|
|
522
|
-
# Make the gRPC call
|
|
523
|
-
async with GrpcClient() as client:
|
|
524
|
-
response = await client.assistants.SetLatest(request)
|
|
525
|
-
|
|
526
|
-
# Convert and yield the result
|
|
527
|
-
assistant = proto_to_assistant(response)
|
|
528
|
-
|
|
529
|
-
async def generate_result():
|
|
530
|
-
yield assistant
|
|
531
|
-
|
|
532
|
-
return generate_result()
|
|
533
|
-
|
|
534
|
-
@staticmethod
|
|
535
|
-
async def get_versions(
|
|
536
|
-
conn, # Not used in gRPC implementation
|
|
537
|
-
assistant_id: UUID | str,
|
|
538
|
-
metadata: MetadataInput,
|
|
539
|
-
limit: int,
|
|
540
|
-
offset: int,
|
|
541
|
-
ctx: Any = None,
|
|
542
|
-
) -> AsyncIterator[Assistant]: # type: ignore[return-value]
|
|
543
|
-
"""Get all versions of assistant via gRPC."""
|
|
544
|
-
# Handle auth filters
|
|
545
|
-
auth_filters = await Assistants.handle_event(
|
|
546
|
-
ctx,
|
|
547
|
-
"search",
|
|
548
|
-
{"assistant_id": str(assistant_id), "metadata": metadata},
|
|
549
|
-
)
|
|
550
|
-
|
|
551
|
-
# Build the gRPC request
|
|
552
|
-
request = pb.GetAssistantVersionsRequest(
|
|
553
|
-
assistant_id=str(assistant_id),
|
|
554
|
-
filters=auth_filters or {},
|
|
555
|
-
metadata=dict_to_struct(metadata or {}),
|
|
556
|
-
limit=limit,
|
|
557
|
-
offset=offset,
|
|
558
|
-
)
|
|
559
|
-
|
|
560
|
-
# Make the gRPC call
|
|
561
|
-
async with GrpcClient() as client:
|
|
562
|
-
response = await client.assistants.GetVersions(request)
|
|
563
|
-
|
|
564
|
-
# Convert and yield the results
|
|
565
|
-
async def generate_results():
|
|
566
|
-
for version in response.versions:
|
|
567
|
-
# Preserve None for optional scalar fields by checking presence
|
|
568
|
-
version_description = (
|
|
569
|
-
version.description if version.HasField("description") else None
|
|
570
|
-
)
|
|
571
|
-
yield {
|
|
572
|
-
"assistant_id": version.assistant_id,
|
|
573
|
-
"graph_id": version.graph_id,
|
|
574
|
-
"version": version.version,
|
|
575
|
-
"created_at": version.created_at.ToDatetime(tzinfo=UTC),
|
|
576
|
-
"config": _runnable_config_to_user_dict(version.config),
|
|
577
|
-
"context": struct_to_dict(version.context),
|
|
578
|
-
"metadata": struct_to_dict(version.metadata),
|
|
579
|
-
"name": version.name,
|
|
580
|
-
"description": version_description,
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return generate_results()
|
|
584
|
-
|
|
585
|
-
@staticmethod
|
|
586
|
-
async def count(
|
|
587
|
-
conn, # Not used in gRPC implementation
|
|
588
|
-
*,
|
|
589
|
-
graph_id: str | None = None,
|
|
590
|
-
metadata: MetadataInput = None,
|
|
591
|
-
ctx: Any = None,
|
|
592
|
-
) -> int: # type: ignore[return-value]
|
|
593
|
-
"""Count assistants via gRPC."""
|
|
594
|
-
# Handle auth filters
|
|
595
|
-
auth_filters = await Assistants.handle_event(
|
|
596
|
-
ctx, "search", {"graph_id": graph_id, "metadata": metadata}
|
|
597
|
-
)
|
|
598
|
-
|
|
599
|
-
# Build the gRPC request
|
|
600
|
-
request = pb.CountAssistantsRequest(
|
|
601
|
-
filters=auth_filters or {},
|
|
602
|
-
graph_id=graph_id,
|
|
603
|
-
metadata=dict_to_struct(metadata or {}),
|
|
604
|
-
)
|
|
605
|
-
|
|
606
|
-
# Make the gRPC call
|
|
607
|
-
async with GrpcClient() as client:
|
|
608
|
-
response = await client.assistants.Count(request)
|
|
609
|
-
|
|
610
|
-
return int(response.count)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|