livekit-plugins-anthropic 0.2.10__py3-none-any.whl → 0.2.11__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.
@@ -53,6 +53,8 @@ from .models import (
53
53
  ChatModels,
54
54
  )
55
55
 
56
+ CACHE_CONTROL_EPHEMERAL = anthropic.types.CacheControlEphemeralParam(type="ephemeral")
57
+
56
58
 
57
59
  @dataclass
58
60
  class LLMOptions:
@@ -61,6 +63,8 @@ class LLMOptions:
61
63
  temperature: float | None
62
64
  parallel_tool_calls: bool | None
63
65
  tool_choice: Union[ToolChoice, Literal["auto", "required", "none"]] | None
66
+ caching: Literal["ephemeral"] | None = None
67
+ """If set to "ephemeral", the system prompt, tools, and chat history will be cached."""
64
68
 
65
69
 
66
70
  class LLM(llm.LLM):
@@ -75,12 +79,23 @@ class LLM(llm.LLM):
75
79
  temperature: float | None = None,
76
80
  parallel_tool_calls: bool | None = None,
77
81
  tool_choice: Union[ToolChoice, Literal["auto", "required", "none"]] = "auto",
82
+ caching: Literal["ephemeral"] | None = None,
78
83
  ) -> None:
79
84
  """
80
85
  Create a new instance of Anthropic LLM.
81
86
 
82
87
  ``api_key`` must be set to your Anthropic API key, either using the argument or by setting
83
88
  the ``ANTHROPIC_API_KEY`` environmental variable.
89
+
90
+ model (str | ChatModels): The model to use. Defaults to "claude-3-5-sonnet-20241022".
91
+ api_key (str | None): The Anthropic API key. Defaults to the ANTHROPIC_API_KEY environment variable.
92
+ base_url (str | None): The base URL for the Anthropic API. Defaults to None.
93
+ user (str | None): The user for the Anthropic API. Defaults to None.
94
+ client (anthropic.AsyncClient | None): The Anthropic client to use. Defaults to None.
95
+ temperature (float | None): The temperature for the Anthropic API. Defaults to None.
96
+ parallel_tool_calls (bool | None): Whether to parallelize tool calls. Defaults to None.
97
+ tool_choice (Union[ToolChoice, Literal["auto", "required", "none"]] | None): The tool choice for the Anthropic API. Defaults to "auto".
98
+ caching (Literal["ephemeral"] | None): If set to "ephemeral", caching will be enabled for the system prompt, tools, and chat history.
84
99
  """
85
100
 
86
101
  super().__init__(
@@ -101,6 +116,7 @@ class LLM(llm.LLM):
101
116
  temperature=temperature,
102
117
  parallel_tool_calls=parallel_tool_calls,
103
118
  tool_choice=tool_choice,
119
+ caching=caching,
104
120
  )
105
121
  self._client = client or anthropic.AsyncClient(
106
122
  api_key=api_key,
@@ -138,8 +154,20 @@ class LLM(llm.LLM):
138
154
  opts: dict[str, Any] = dict()
139
155
  if fnc_ctx and len(fnc_ctx.ai_functions) > 0:
140
156
  fncs_desc: list[anthropic.types.ToolParam] = []
141
- for fnc in fnc_ctx.ai_functions.values():
142
- fncs_desc.append(_build_function_description(fnc))
157
+ for i, fnc in enumerate(fnc_ctx.ai_functions.values()):
158
+ # caching last tool will cache all the tools if caching is enabled
159
+ cache_ctrl = (
160
+ CACHE_CONTROL_EPHEMERAL
161
+ if (i == len(fnc_ctx.ai_functions) - 1)
162
+ and self._opts.caching == "ephemeral"
163
+ else None
164
+ )
165
+ fncs_desc.append(
166
+ _build_function_description(
167
+ fnc,
168
+ cache_ctrl=cache_ctrl,
169
+ )
170
+ )
143
171
 
144
172
  opts["tools"] = fncs_desc
145
173
  if tool_choice is not None:
@@ -157,13 +185,19 @@ class LLM(llm.LLM):
157
185
  anthropic_tool_choice["disable_parallel_tool_use"] = True
158
186
  opts["tool_choice"] = anthropic_tool_choice
159
187
 
160
- latest_system_message = _latest_system_message(chat_ctx)
161
- anthropic_ctx = _build_anthropic_context(chat_ctx.messages, id(self))
188
+ latest_system_message: anthropic.types.TextBlockParam = _latest_system_message(
189
+ chat_ctx, caching=self._opts.caching
190
+ )
191
+ anthropic_ctx = _build_anthropic_context(
192
+ chat_ctx.messages,
193
+ id(self),
194
+ caching=self._opts.caching,
195
+ )
162
196
  collaped_anthropic_ctx = _merge_messages(anthropic_ctx)
163
197
 
164
198
  stream = self._client.messages.create(
165
199
  max_tokens=opts.get("max_tokens", 1024),
166
- system=latest_system_message,
200
+ system=[latest_system_message],
167
201
  messages=collaped_anthropic_ctx,
168
202
  model=self._opts.model,
169
203
  temperature=temperature or anthropic.NOT_GIVEN,
@@ -209,6 +243,8 @@ class LLMStream(llm.LLMStream):
209
243
  self._request_id: str = ""
210
244
  self._ignoring_cot = False # ignore chain of thought
211
245
  self._input_tokens = 0
246
+ self._cache_creation_tokens = 0
247
+ self._cache_read_tokens = 0
212
248
  self._output_tokens = 0
213
249
 
214
250
  async def _run(self) -> None:
@@ -230,7 +266,12 @@ class LLMStream(llm.LLMStream):
230
266
  usage=llm.CompletionUsage(
231
267
  completion_tokens=self._output_tokens,
232
268
  prompt_tokens=self._input_tokens,
233
- total_tokens=self._input_tokens + self._output_tokens,
269
+ total_tokens=self._input_tokens
270
+ + self._output_tokens
271
+ + self._cache_creation_tokens
272
+ + self._cache_read_tokens,
273
+ cache_creation_input_tokens=self._cache_creation_tokens,
274
+ cache_read_input_tokens=self._cache_read_tokens,
234
275
  ),
235
276
  )
236
277
  )
@@ -253,6 +294,12 @@ class LLMStream(llm.LLMStream):
253
294
  self._request_id = event.message.id
254
295
  self._input_tokens = event.message.usage.input_tokens
255
296
  self._output_tokens = event.message.usage.output_tokens
297
+ if event.message.usage.cache_creation_input_tokens:
298
+ self._cache_creation_tokens = (
299
+ event.message.usage.cache_creation_input_tokens
300
+ )
301
+ if event.message.usage.cache_read_input_tokens:
302
+ self._cache_read_tokens = event.message.usage.cache_read_input_tokens
256
303
  elif event.type == "message_delta":
257
304
  self._output_tokens += event.usage.output_tokens
258
305
  elif event.type == "content_block_start":
@@ -317,7 +364,9 @@ class LLMStream(llm.LLMStream):
317
364
  return None
318
365
 
319
366
 
320
- def _latest_system_message(chat_ctx: llm.ChatContext) -> str:
367
+ def _latest_system_message(
368
+ chat_ctx: llm.ChatContext, caching: Literal["ephemeral"] | None = None
369
+ ) -> anthropic.types.TextBlockParam:
321
370
  latest_system_message: llm.ChatMessage | None = None
322
371
  for m in chat_ctx.messages:
323
372
  if m.role == "system":
@@ -332,7 +381,12 @@ def _latest_system_message(chat_ctx: llm.ChatContext) -> str:
332
381
  latest_system_str = " ".join(
333
382
  [c for c in latest_system_message.content if isinstance(c, str)]
334
383
  )
335
- return latest_system_str
384
+ system_text_block = anthropic.types.TextBlockParam(
385
+ text=latest_system_str,
386
+ type="text",
387
+ cache_control=CACHE_CONTROL_EPHEMERAL if caching == "ephemeral" else None,
388
+ )
389
+ return system_text_block
336
390
 
337
391
 
338
392
  def _merge_messages(
@@ -362,18 +416,29 @@ def _merge_messages(
362
416
 
363
417
 
364
418
  def _build_anthropic_context(
365
- chat_ctx: List[llm.ChatMessage], cache_key: Any
419
+ chat_ctx: List[llm.ChatMessage],
420
+ cache_key: Any,
421
+ caching: Literal["ephemeral"] | None,
366
422
  ) -> List[anthropic.types.MessageParam]:
367
423
  result: List[anthropic.types.MessageParam] = []
368
- for msg in chat_ctx:
369
- a_msg = _build_anthropic_message(msg, cache_key, chat_ctx)
424
+ for i, msg in enumerate(chat_ctx):
425
+ # caching last message will cache whole chat history if caching is enabled
426
+ cache_ctrl = (
427
+ CACHE_CONTROL_EPHEMERAL
428
+ if ((i == len(chat_ctx) - 1) and caching == "ephemeral")
429
+ else None
430
+ )
431
+ a_msg = _build_anthropic_message(msg, cache_key, cache_ctrl=cache_ctrl)
432
+
370
433
  if a_msg:
371
434
  result.append(a_msg)
372
435
  return result
373
436
 
374
437
 
375
438
  def _build_anthropic_message(
376
- msg: llm.ChatMessage, cache_key: Any, chat_ctx: List[llm.ChatMessage]
439
+ msg: llm.ChatMessage,
440
+ cache_key: Any,
441
+ cache_ctrl: anthropic.types.CacheControlEphemeralParam | None,
377
442
  ) -> anthropic.types.MessageParam | None:
378
443
  if msg.role == "user" or msg.role == "assistant":
379
444
  a_msg: anthropic.types.MessageParam = {
@@ -386,22 +451,27 @@ def _build_anthropic_message(
386
451
  # add content if provided
387
452
  if isinstance(msg.content, str) and msg.content:
388
453
  a_msg["content"].append(
389
- anthropic.types.TextBlock(
454
+ anthropic.types.TextBlockParam(
390
455
  text=msg.content,
391
456
  type="text",
457
+ cache_control=cache_ctrl,
392
458
  )
393
459
  )
394
460
  elif isinstance(msg.content, list):
395
461
  for cnt in msg.content:
396
462
  if isinstance(cnt, str) and cnt:
397
- content: anthropic.types.TextBlock = anthropic.types.TextBlock(
398
- text=cnt,
399
- type="text",
463
+ content: anthropic.types.TextBlockParam = (
464
+ anthropic.types.TextBlockParam(
465
+ text=cnt,
466
+ type="text",
467
+ cache_control=cache_ctrl,
468
+ )
400
469
  )
401
470
  a_content.append(content)
402
471
  elif isinstance(cnt, llm.ChatImage):
403
- a_content.append(_build_anthropic_image_content(cnt, cache_key))
404
-
472
+ a_content.append(
473
+ _build_anthropic_image_content(cnt, cache_key, cache_ctrl)
474
+ )
405
475
  if msg.tool_calls is not None:
406
476
  for fnc in msg.tool_calls:
407
477
  tool_use = anthropic.types.ToolUseBlockParam(
@@ -409,6 +479,7 @@ def _build_anthropic_message(
409
479
  type="tool_use",
410
480
  name=fnc.function_info.name,
411
481
  input=fnc.arguments,
482
+ cache_control=cache_ctrl,
412
483
  )
413
484
  a_content.append(tool_use)
414
485
 
@@ -427,6 +498,7 @@ def _build_anthropic_message(
427
498
  type="tool_result",
428
499
  content=msg.content,
429
500
  is_error=msg.tool_exception is not None,
501
+ cache_control=cache_ctrl,
430
502
  )
431
503
  return {
432
504
  "role": "user",
@@ -437,7 +509,9 @@ def _build_anthropic_message(
437
509
 
438
510
 
439
511
  def _build_anthropic_image_content(
440
- image: llm.ChatImage, cache_key: Any
512
+ image: llm.ChatImage,
513
+ cache_key: Any,
514
+ cache_ctrl: anthropic.types.CacheControlEphemeralParam | None,
441
515
  ) -> anthropic.types.ImageBlockParam:
442
516
  if isinstance(image.image, str): # image is a URL
443
517
  if not image.image.startswith("data:"):
@@ -463,6 +537,7 @@ def _build_anthropic_image_content(
463
537
  media_type,
464
538
  ),
465
539
  },
540
+ "cache_control": cache_ctrl,
466
541
  }
467
542
  except (ValueError, IndexError) as e:
468
543
  raise ValueError(
@@ -490,6 +565,7 @@ def _build_anthropic_image_content(
490
565
  "data": image._cache[cache_key],
491
566
  "media_type": "image/jpeg",
492
567
  },
568
+ "cache_control": cache_ctrl,
493
569
  }
494
570
 
495
571
  raise ValueError(
@@ -499,6 +575,7 @@ def _build_anthropic_image_content(
499
575
 
500
576
  def _build_function_description(
501
577
  fnc_info: llm.function_context.FunctionInfo,
578
+ cache_ctrl: anthropic.types.CacheControlEphemeralParam | None,
502
579
  ) -> anthropic.types.ToolParam:
503
580
  def build_schema_field(arg_info: llm.function_context.FunctionArgInfo):
504
581
  def type2str(t: type) -> str:
@@ -520,7 +597,7 @@ def _build_function_description(
520
597
  if arg_info.description:
521
598
  p["description"] = arg_info.description
522
599
 
523
- is_optional, inner_th = _is_optional_type(arg_info.type)
600
+ _, inner_th = _is_optional_type(arg_info.type)
524
601
 
525
602
  if get_origin(inner_th) is list:
526
603
  inner_type = get_args(inner_th)[0]
@@ -542,8 +619,9 @@ def _build_function_description(
542
619
  for arg_info in fnc_info.arguments.values():
543
620
  input_schema[arg_info.name] = build_schema_field(arg_info)
544
621
 
545
- return {
546
- "name": fnc_info.name,
547
- "description": fnc_info.description,
548
- "input_schema": input_schema,
549
- }
622
+ return anthropic.types.ToolParam(
623
+ name=fnc_info.name,
624
+ description=fnc_info.description,
625
+ input_schema=input_schema,
626
+ cache_control=cache_ctrl,
627
+ )
@@ -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.10"
15
+ __version__ = "0.2.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: livekit-plugins-anthropic
3
- Version: 0.2.10
3
+ Version: 0.2.11
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
@@ -0,0 +1,10 @@
1
+ livekit/plugins/anthropic/__init__.py,sha256=1WCyNEaR6qBsX54qJQM0SeY-QHIucww16PLXcSnMqRo,1175
2
+ livekit/plugins/anthropic/llm.py,sha256=JgY7x1DlngrQNsXss_Vc0VMNge4jza3D3-tde7WtNwU,23071
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=tQf5iSQsEO0Vr1TimFhAqVDGkoj_Na4Qfj_agnoO3NQ,601
7
+ livekit_plugins_anthropic-0.2.11.dist-info/METADATA,sha256=IY-JVBfw4xdAXlZZfIYqcOqBrBKAPGDUBP34cItva60,1481
8
+ livekit_plugins_anthropic-0.2.11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
+ livekit_plugins_anthropic-0.2.11.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
10
+ livekit_plugins_anthropic-0.2.11.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- livekit/plugins/anthropic/__init__.py,sha256=1WCyNEaR6qBsX54qJQM0SeY-QHIucww16PLXcSnMqRo,1175
2
- livekit/plugins/anthropic/llm.py,sha256=xRayMqq_FXFGZwskpqcINKVCbXL-754Jh2CJMWvxJnY,19261
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=vT0ONEJ_8wtOHcTDpZ96O0mQtrdNrO2FOuqDfAOF_bI,601
7
- livekit_plugins_anthropic-0.2.10.dist-info/METADATA,sha256=8DF-B_YzfayulYZMpOB2_smktfH8s9gKXXXeccz5J4U,1481
8
- livekit_plugins_anthropic-0.2.10.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
- livekit_plugins_anthropic-0.2.10.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
10
- livekit_plugins_anthropic-0.2.10.dist-info/RECORD,,