livekit-plugins-anthropic 0.2.7__py3-none-any.whl → 0.2.8__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.
@@ -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
@@ -427,12 +431,36 @@ def _build_anthropic_message(
427
431
  def _build_anthropic_image_content(
428
432
  image: llm.ChatImage, cache_key: Any
429
433
  ) -> anthropic.types.ImageBlockParam:
430
- if isinstance(image.image, str): # image url
431
- logger.warning(
432
- "ChatImage with url is not yet supported by the LiveKit Anthropic plugin, skipping image '%s'",
433
- image.image,
434
- )
435
- elif isinstance(image.image, rtc.VideoFrame): # VideoFrame
434
+ if isinstance(image.image, str): # image is a URL
435
+ if not image.image.startswith("data:"):
436
+ raise ValueError("LiveKit Anthropic Plugin: Image URLs must be data URLs")
437
+
438
+ try:
439
+ header, b64_data = image.image.split(",", 1)
440
+ media_type = header.split(";")[0].split(":")[1]
441
+
442
+ supported_types = {"image/jpeg", "image/png", "image/webp", "image/gif"}
443
+ if media_type not in supported_types:
444
+ raise ValueError(
445
+ f"LiveKit Anthropic Plugin: Unsupported media type {media_type}. Must be jpeg, png, webp, or gif"
446
+ )
447
+
448
+ return {
449
+ "type": "image",
450
+ "source": {
451
+ "type": "base64",
452
+ "data": b64_data,
453
+ "media_type": cast(
454
+ Literal["image/jpeg", "image/png", "image/gif", "image/webp"],
455
+ media_type,
456
+ ),
457
+ },
458
+ }
459
+ except (ValueError, IndexError) as e:
460
+ raise ValueError(
461
+ f"LiveKit Anthropic Plugin: Invalid image data URL {str(e)}"
462
+ )
463
+ elif isinstance(image.image, rtc.VideoFrame): # image is a VideoFrame
436
464
  if cache_key not in image._cache:
437
465
  # inside our internal implementation, we allow to put extra metadata to
438
466
  # each ChatImage (avoid to reencode each time we do a chatcompletion request)
@@ -441,7 +469,7 @@ def _build_anthropic_image_content(
441
469
  opts.resize_options = utils.images.ResizeOptions(
442
470
  width=image.inference_width,
443
471
  height=image.inference_height,
444
- strategy="center_aspect_fit",
472
+ strategy="scale_aspect_fit",
445
473
  )
446
474
 
447
475
  encoded_data = utils.images.encode(image.image, opts)
@@ -456,65 +484,8 @@ def _build_anthropic_image_content(
456
484
  },
457
485
  }
458
486
 
459
- raise ValueError(f"unknown image type {type(image.image)}")
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,
487
+ raise ValueError(
488
+ "LiveKit Anthropic Plugin: ChatImage must be an rtc.VideoFrame or a data URL"
518
489
  )
519
490
 
520
491
 
@@ -541,8 +512,10 @@ def _build_function_description(
541
512
  if arg_info.description:
542
513
  p["description"] = arg_info.description
543
514
 
544
- if get_origin(arg_info.type) is list:
545
- inner_type = get_args(arg_info.type)[0]
515
+ is_optional, inner_th = _is_optional_type(arg_info.type)
516
+
517
+ if get_origin(inner_th) is list:
518
+ inner_type = get_args(inner_th)[0]
546
519
  p["type"] = "array"
547
520
  p["items"] = {}
548
521
  p["items"]["type"] = type2str(inner_type)
@@ -550,7 +523,7 @@ def _build_function_description(
550
523
  if arg_info.choices:
551
524
  p["items"]["enum"] = arg_info.choices
552
525
  else:
553
- p["type"] = type2str(arg_info.type)
526
+ p["type"] = type2str(inner_th)
554
527
  if arg_info.choices:
555
528
  p["enum"] = arg_info.choices
556
529
 
@@ -566,31 +539,3 @@ def _build_function_description(
566
539
  "description": fnc_info.description,
567
540
  "input_schema": input_schema,
568
541
  }
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
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "0.2.7"
15
+ __version__ = "0.2.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: livekit-plugins-anthropic
3
- Version: 0.2.7
3
+ Version: 0.2.8
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.11
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=e65Z_YchNHCXN2F1kKb8lczfSY1_Ak8Y_94nT12pZGI,18975
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=711Prlpzg5p2xCBjcE2dctFtzW_seKcOBI5-dvNUVK4,600
7
+ livekit_plugins_anthropic-0.2.8.dist-info/METADATA,sha256=xG06Y2Xf9RjmmBqJ314_6ZmFlEM0ZCo00K82wJHEHHo,1265
8
+ livekit_plugins_anthropic-0.2.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
9
+ livekit_plugins_anthropic-0.2.8.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
10
+ livekit_plugins_anthropic-0.2.8.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,,