livekit-plugins-anthropic 0.2.7__py3-none-any.whl → 0.2.9__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.
- livekit/plugins/anthropic/llm.py +47 -100
- livekit/plugins/anthropic/version.py +1 -1
- {livekit_plugins_anthropic-0.2.7.dist-info → livekit_plugins_anthropic-0.2.9.dist-info}/METADATA +2 -2
- livekit_plugins_anthropic-0.2.9.dist-info/RECORD +10 -0
- livekit_plugins_anthropic-0.2.7.dist-info/RECORD +0 -10
- {livekit_plugins_anthropic-0.2.7.dist-info → livekit_plugins_anthropic-0.2.9.dist-info}/WHEEL +0 -0
- {livekit_plugins_anthropic-0.2.7.dist-info → livekit_plugins_anthropic-0.2.9.dist-info}/top_level.txt +0 -0
livekit/plugins/anthropic/llm.py
CHANGED
@@ -24,8 +24,8 @@ from typing import (
|
|
24
24
|
Awaitable,
|
25
25
|
List,
|
26
26
|
Literal,
|
27
|
-
Tuple,
|
28
27
|
Union,
|
28
|
+
cast,
|
29
29
|
get_args,
|
30
30
|
get_origin,
|
31
31
|
)
|
@@ -40,6 +40,10 @@ from livekit.agents import (
|
|
40
40
|
utils,
|
41
41
|
)
|
42
42
|
from livekit.agents.llm import ToolChoice
|
43
|
+
from livekit.agents.llm.function_context import (
|
44
|
+
_create_ai_function_info,
|
45
|
+
_is_optional_type,
|
46
|
+
)
|
43
47
|
from livekit.agents.types import DEFAULT_API_CONNECT_OPTIONS, APIConnectOptions
|
44
48
|
|
45
49
|
import anthropic
|
@@ -202,6 +206,7 @@ class LLMStream(llm.LLMStream):
|
|
202
206
|
self._output_tokens = 0
|
203
207
|
|
204
208
|
async def _run(self) -> None:
|
209
|
+
retryable = True
|
205
210
|
try:
|
206
211
|
if not self._anthropic_stream:
|
207
212
|
self._anthropic_stream = await self._awaitable_anthropic_stream
|
@@ -211,6 +216,7 @@ class LLMStream(llm.LLMStream):
|
|
211
216
|
chat_chunk = self._parse_event(event)
|
212
217
|
if chat_chunk is not None:
|
213
218
|
self._event_ch.send_nowait(chat_chunk)
|
219
|
+
retryable = False
|
214
220
|
|
215
221
|
self._event_ch.send_nowait(
|
216
222
|
llm.ChatChunk(
|
@@ -223,7 +229,7 @@ class LLMStream(llm.LLMStream):
|
|
223
229
|
)
|
224
230
|
)
|
225
231
|
except anthropic.APITimeoutError:
|
226
|
-
raise APITimeoutError()
|
232
|
+
raise APITimeoutError(retryable=retryable)
|
227
233
|
except anthropic.APIStatusError as e:
|
228
234
|
raise APIStatusError(
|
229
235
|
e.message,
|
@@ -232,7 +238,7 @@ class LLMStream(llm.LLMStream):
|
|
232
238
|
body=e.body,
|
233
239
|
)
|
234
240
|
except Exception as e:
|
235
|
-
raise APIConnectionError() from e
|
241
|
+
raise APIConnectionError(retryable=retryable) from e
|
236
242
|
|
237
243
|
def _parse_event(
|
238
244
|
self, event: anthropic.types.RawMessageStreamEvent
|
@@ -427,12 +433,36 @@ def _build_anthropic_message(
|
|
427
433
|
def _build_anthropic_image_content(
|
428
434
|
image: llm.ChatImage, cache_key: Any
|
429
435
|
) -> anthropic.types.ImageBlockParam:
|
430
|
-
if isinstance(image.image, str): # image
|
431
|
-
|
432
|
-
"
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
+
if isinstance(image.image, str): # image is a URL
|
437
|
+
if not image.image.startswith("data:"):
|
438
|
+
raise ValueError("LiveKit Anthropic Plugin: Image URLs must be data URLs")
|
439
|
+
|
440
|
+
try:
|
441
|
+
header, b64_data = image.image.split(",", 1)
|
442
|
+
media_type = header.split(";")[0].split(":")[1]
|
443
|
+
|
444
|
+
supported_types = {"image/jpeg", "image/png", "image/webp", "image/gif"}
|
445
|
+
if media_type not in supported_types:
|
446
|
+
raise ValueError(
|
447
|
+
f"LiveKit Anthropic Plugin: Unsupported media type {media_type}. Must be jpeg, png, webp, or gif"
|
448
|
+
)
|
449
|
+
|
450
|
+
return {
|
451
|
+
"type": "image",
|
452
|
+
"source": {
|
453
|
+
"type": "base64",
|
454
|
+
"data": b64_data,
|
455
|
+
"media_type": cast(
|
456
|
+
Literal["image/jpeg", "image/png", "image/gif", "image/webp"],
|
457
|
+
media_type,
|
458
|
+
),
|
459
|
+
},
|
460
|
+
}
|
461
|
+
except (ValueError, IndexError) as e:
|
462
|
+
raise ValueError(
|
463
|
+
f"LiveKit Anthropic Plugin: Invalid image data URL {str(e)}"
|
464
|
+
)
|
465
|
+
elif isinstance(image.image, rtc.VideoFrame): # image is a VideoFrame
|
436
466
|
if cache_key not in image._cache:
|
437
467
|
# inside our internal implementation, we allow to put extra metadata to
|
438
468
|
# each ChatImage (avoid to reencode each time we do a chatcompletion request)
|
@@ -441,7 +471,7 @@ def _build_anthropic_image_content(
|
|
441
471
|
opts.resize_options = utils.images.ResizeOptions(
|
442
472
|
width=image.inference_width,
|
443
473
|
height=image.inference_height,
|
444
|
-
strategy="
|
474
|
+
strategy="scale_aspect_fit",
|
445
475
|
)
|
446
476
|
|
447
477
|
encoded_data = utils.images.encode(image.image, opts)
|
@@ -456,65 +486,8 @@ def _build_anthropic_image_content(
|
|
456
486
|
},
|
457
487
|
}
|
458
488
|
|
459
|
-
raise ValueError(
|
460
|
-
|
461
|
-
|
462
|
-
def _create_ai_function_info(
|
463
|
-
fnc_ctx: llm.function_context.FunctionContext,
|
464
|
-
tool_call_id: str,
|
465
|
-
fnc_name: str,
|
466
|
-
raw_arguments: str, # JSON string
|
467
|
-
) -> llm.function_context.FunctionCallInfo:
|
468
|
-
if fnc_name not in fnc_ctx.ai_functions:
|
469
|
-
raise ValueError(f"AI function {fnc_name} not found")
|
470
|
-
|
471
|
-
parsed_arguments: dict[str, Any] = {}
|
472
|
-
try:
|
473
|
-
if raw_arguments: # ignore empty string
|
474
|
-
parsed_arguments = json.loads(raw_arguments)
|
475
|
-
except json.JSONDecodeError:
|
476
|
-
raise ValueError(
|
477
|
-
f"AI function {fnc_name} received invalid JSON arguments - {raw_arguments}"
|
478
|
-
)
|
479
|
-
|
480
|
-
fnc_info = fnc_ctx.ai_functions[fnc_name]
|
481
|
-
|
482
|
-
# Ensure all necessary arguments are present and of the correct type.
|
483
|
-
sanitized_arguments: dict[str, Any] = {}
|
484
|
-
for arg_info in fnc_info.arguments.values():
|
485
|
-
if arg_info.name not in parsed_arguments:
|
486
|
-
if arg_info.default is inspect.Parameter.empty:
|
487
|
-
raise ValueError(
|
488
|
-
f"AI function {fnc_name} missing required argument {arg_info.name}"
|
489
|
-
)
|
490
|
-
continue
|
491
|
-
|
492
|
-
arg_value = parsed_arguments[arg_info.name]
|
493
|
-
if get_origin(arg_info.type) is not None:
|
494
|
-
if not isinstance(arg_value, list):
|
495
|
-
raise ValueError(
|
496
|
-
f"AI function {fnc_name} argument {arg_info.name} should be a list"
|
497
|
-
)
|
498
|
-
|
499
|
-
inner_type = get_args(arg_info.type)[0]
|
500
|
-
sanitized_value = [
|
501
|
-
_sanitize_primitive(
|
502
|
-
value=v, expected_type=inner_type, choices=arg_info.choices
|
503
|
-
)
|
504
|
-
for v in arg_value
|
505
|
-
]
|
506
|
-
else:
|
507
|
-
sanitized_value = _sanitize_primitive(
|
508
|
-
value=arg_value, expected_type=arg_info.type, choices=arg_info.choices
|
509
|
-
)
|
510
|
-
|
511
|
-
sanitized_arguments[arg_info.name] = sanitized_value
|
512
|
-
|
513
|
-
return llm.function_context.FunctionCallInfo(
|
514
|
-
tool_call_id=tool_call_id,
|
515
|
-
raw_arguments=raw_arguments,
|
516
|
-
function_info=fnc_info,
|
517
|
-
arguments=sanitized_arguments,
|
489
|
+
raise ValueError(
|
490
|
+
"LiveKit Anthropic Plugin: ChatImage must be an rtc.VideoFrame or a data URL"
|
518
491
|
)
|
519
492
|
|
520
493
|
|
@@ -541,8 +514,10 @@ def _build_function_description(
|
|
541
514
|
if arg_info.description:
|
542
515
|
p["description"] = arg_info.description
|
543
516
|
|
544
|
-
|
545
|
-
|
517
|
+
is_optional, inner_th = _is_optional_type(arg_info.type)
|
518
|
+
|
519
|
+
if get_origin(inner_th) is list:
|
520
|
+
inner_type = get_args(inner_th)[0]
|
546
521
|
p["type"] = "array"
|
547
522
|
p["items"] = {}
|
548
523
|
p["items"]["type"] = type2str(inner_type)
|
@@ -550,7 +525,7 @@ def _build_function_description(
|
|
550
525
|
if arg_info.choices:
|
551
526
|
p["items"]["enum"] = arg_info.choices
|
552
527
|
else:
|
553
|
-
p["type"] = type2str(
|
528
|
+
p["type"] = type2str(inner_th)
|
554
529
|
if arg_info.choices:
|
555
530
|
p["enum"] = arg_info.choices
|
556
531
|
|
@@ -566,31 +541,3 @@ def _build_function_description(
|
|
566
541
|
"description": fnc_info.description,
|
567
542
|
"input_schema": input_schema,
|
568
543
|
}
|
569
|
-
|
570
|
-
|
571
|
-
def _sanitize_primitive(
|
572
|
-
*, value: Any, expected_type: type, choices: Tuple[Any] | None
|
573
|
-
) -> Any:
|
574
|
-
if expected_type is str:
|
575
|
-
if not isinstance(value, str):
|
576
|
-
raise ValueError(f"expected str, got {type(value)}")
|
577
|
-
elif expected_type in (int, float):
|
578
|
-
if not isinstance(value, (int, float)):
|
579
|
-
raise ValueError(f"expected number, got {type(value)}")
|
580
|
-
|
581
|
-
if expected_type is int:
|
582
|
-
if value % 1 != 0:
|
583
|
-
raise ValueError("expected int, got float")
|
584
|
-
|
585
|
-
value = int(value)
|
586
|
-
elif expected_type is float:
|
587
|
-
value = float(value)
|
588
|
-
|
589
|
-
elif expected_type is bool:
|
590
|
-
if not isinstance(value, bool):
|
591
|
-
raise ValueError(f"expected bool, got {type(value)}")
|
592
|
-
|
593
|
-
if choices and value not in choices:
|
594
|
-
raise ValueError(f"invalid value {value}, not in {choices}")
|
595
|
-
|
596
|
-
return value
|
{livekit_plugins_anthropic-0.2.7.dist-info → livekit_plugins_anthropic-0.2.9.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: livekit-plugins-anthropic
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.9
|
4
4
|
Summary: Agent Framework plugin for services from Anthropic
|
5
5
|
Home-page: https://github.com/livekit/agents
|
6
6
|
License: Apache-2.0
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
19
19
|
Classifier: Programming Language :: Python :: 3 :: Only
|
20
20
|
Requires-Python: >=3.9.0
|
21
21
|
Description-Content-Type: text/markdown
|
22
|
-
Requires-Dist: livekit-agents>=0.
|
22
|
+
Requires-Dist: livekit-agents>=0.12.3
|
23
23
|
Requires-Dist: anthropic>=0.34
|
24
24
|
|
25
25
|
# LiveKit Plugins Anthropic
|
@@ -0,0 +1,10 @@
|
|
1
|
+
livekit/plugins/anthropic/__init__.py,sha256=1WCyNEaR6qBsX54qJQM0SeY-QHIucww16PLXcSnMqRo,1175
|
2
|
+
livekit/plugins/anthropic/llm.py,sha256=bl3cLKLvz_hTf_fL9PfDLMl6uPvFkRJTn_R33T39ZQw,19080
|
3
|
+
livekit/plugins/anthropic/log.py,sha256=fG1pYSY88AnT738gZrmzF9FO4l4BdGENj3VKHMQB3Yo,72
|
4
|
+
livekit/plugins/anthropic/models.py,sha256=wyTr2nl6SL4ylN6s4mHJcqtmgV2mjJysZo89FknWdhI,213
|
5
|
+
livekit/plugins/anthropic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
livekit/plugins/anthropic/version.py,sha256=H-ib9_H1SbJY8B6-psDc7ndkyQeTmdfDstZPRVGab34,600
|
7
|
+
livekit_plugins_anthropic-0.2.9.dist-info/METADATA,sha256=7g5vPjgRL8_Nlv1jBKE7SeK9kFrPv4SGWtA8INpvMjk,1265
|
8
|
+
livekit_plugins_anthropic-0.2.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
9
|
+
livekit_plugins_anthropic-0.2.9.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
|
10
|
+
livekit_plugins_anthropic-0.2.9.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
livekit/plugins/anthropic/__init__.py,sha256=1WCyNEaR6qBsX54qJQM0SeY-QHIucww16PLXcSnMqRo,1175
|
2
|
-
livekit/plugins/anthropic/llm.py,sha256=6v_f7G8_zxFnf8D-mIDN-2jYaju8mMPCVnZ485SIuzM,20798
|
3
|
-
livekit/plugins/anthropic/log.py,sha256=fG1pYSY88AnT738gZrmzF9FO4l4BdGENj3VKHMQB3Yo,72
|
4
|
-
livekit/plugins/anthropic/models.py,sha256=wyTr2nl6SL4ylN6s4mHJcqtmgV2mjJysZo89FknWdhI,213
|
5
|
-
livekit/plugins/anthropic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
livekit/plugins/anthropic/version.py,sha256=ctbdsqNKaYDxuDqpKvxGUGiUJhMWF_Cgw8jw_g8sC_0,600
|
7
|
-
livekit_plugins_anthropic-0.2.7.dist-info/METADATA,sha256=bl5blok7UehQompcSjpT1P00MssZH-H8OOC-au_Je1M,1263
|
8
|
-
livekit_plugins_anthropic-0.2.7.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
9
|
-
livekit_plugins_anthropic-0.2.7.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
|
10
|
-
livekit_plugins_anthropic-0.2.7.dist-info/RECORD,,
|
{livekit_plugins_anthropic-0.2.7.dist-info → livekit_plugins_anthropic-0.2.9.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|