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.
- livekit/plugins/anthropic/llm.py +103 -25
- livekit/plugins/anthropic/version.py +1 -1
- {livekit_plugins_anthropic-0.2.10.dist-info → livekit_plugins_anthropic-0.2.11.dist-info}/METADATA +1 -1
- livekit_plugins_anthropic-0.2.11.dist-info/RECORD +10 -0
- livekit_plugins_anthropic-0.2.10.dist-info/RECORD +0 -10
- {livekit_plugins_anthropic-0.2.10.dist-info → livekit_plugins_anthropic-0.2.11.dist-info}/WHEEL +0 -0
- {livekit_plugins_anthropic-0.2.10.dist-info → livekit_plugins_anthropic-0.2.11.dist-info}/top_level.txt +0 -0
livekit/plugins/anthropic/llm.py
CHANGED
@@ -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
|
-
|
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(
|
161
|
-
|
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
|
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(
|
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
|
-
|
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],
|
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
|
-
|
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,
|
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.
|
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.
|
398
|
-
|
399
|
-
|
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(
|
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,
|
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
|
-
|
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
|
-
|
547
|
-
|
548
|
-
|
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
|
+
)
|
@@ -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,,
|
{livekit_plugins_anthropic-0.2.10.dist-info → livekit_plugins_anthropic-0.2.11.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|