livekit-plugins-anthropic 0.2.1__py3-none-any.whl → 0.2.3__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/__init__.py +9 -0
- livekit/plugins/anthropic/llm.py +128 -88
- livekit/plugins/anthropic/version.py +1 -1
- {livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.3.dist-info}/METADATA +3 -3
- livekit_plugins_anthropic-0.2.3.dist-info/RECORD +10 -0
- {livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.3.dist-info}/WHEEL +1 -1
- livekit_plugins_anthropic-0.2.1.dist-info/RECORD +0 -10
- {livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.3.dist-info}/top_level.txt +0 -0
@@ -35,3 +35,12 @@ class AnthropicPlugin(Plugin):
|
|
35
35
|
|
36
36
|
|
37
37
|
Plugin.register_plugin(AnthropicPlugin())
|
38
|
+
|
39
|
+
# Cleanup docs of unexported modules
|
40
|
+
_module = dir()
|
41
|
+
NOT_IN_ALL = [m for m in _module if m not in __all__]
|
42
|
+
|
43
|
+
__pdoc__ = {}
|
44
|
+
|
45
|
+
for n in NOT_IN_ALL:
|
46
|
+
__pdoc__[n] = False
|
livekit/plugins/anthropic/llm.py
CHANGED
@@ -23,7 +23,13 @@ from typing import Any, Awaitable, List, Tuple, get_args, get_origin
|
|
23
23
|
|
24
24
|
import httpx
|
25
25
|
from livekit import rtc
|
26
|
-
from livekit.agents import
|
26
|
+
from livekit.agents import (
|
27
|
+
APIConnectionError,
|
28
|
+
APIStatusError,
|
29
|
+
APITimeoutError,
|
30
|
+
llm,
|
31
|
+
utils,
|
32
|
+
)
|
27
33
|
|
28
34
|
import anthropic
|
29
35
|
|
@@ -37,6 +43,7 @@ from .models import (
|
|
37
43
|
class LLMOptions:
|
38
44
|
model: str | ChatModels
|
39
45
|
user: str | None
|
46
|
+
temperature: float | None
|
40
47
|
|
41
48
|
|
42
49
|
class LLM(llm.LLM):
|
@@ -48,6 +55,7 @@ class LLM(llm.LLM):
|
|
48
55
|
base_url: str | None = None,
|
49
56
|
user: str | None = None,
|
50
57
|
client: anthropic.AsyncClient | None = None,
|
58
|
+
temperature: float | None = None,
|
51
59
|
) -> None:
|
52
60
|
"""
|
53
61
|
Create a new instance of Anthropic LLM.
|
@@ -55,13 +63,14 @@ class LLM(llm.LLM):
|
|
55
63
|
``api_key`` must be set to your Anthropic API key, either using the argument or by setting
|
56
64
|
the ``ANTHROPIC_API_KEY`` environmental variable.
|
57
65
|
"""
|
66
|
+
super().__init__()
|
58
67
|
|
59
68
|
# throw an error on our end
|
60
69
|
api_key = api_key or os.environ.get("ANTHROPIC_API_KEY")
|
61
70
|
if api_key is None:
|
62
71
|
raise ValueError("Anthropic API key is required")
|
63
72
|
|
64
|
-
self._opts = LLMOptions(model=model, user=user)
|
73
|
+
self._opts = LLMOptions(model=model, user=user, temperature=temperature)
|
65
74
|
self._client = client or anthropic.AsyncClient(
|
66
75
|
api_key=api_key,
|
67
76
|
base_url=base_url,
|
@@ -85,6 +94,9 @@ class LLM(llm.LLM):
|
|
85
94
|
n: int | None = 1,
|
86
95
|
parallel_tool_calls: bool | None = None,
|
87
96
|
) -> "LLMStream":
|
97
|
+
if temperature is None:
|
98
|
+
temperature = self._opts.temperature
|
99
|
+
|
88
100
|
opts: dict[str, Any] = dict()
|
89
101
|
if fnc_ctx and len(fnc_ctx.ai_functions) > 0:
|
90
102
|
fncs_desc: list[anthropic.types.ToolParam] = []
|
@@ -99,8 +111,9 @@ class LLM(llm.LLM):
|
|
99
111
|
latest_system_message = _latest_system_message(chat_ctx)
|
100
112
|
anthropic_ctx = _build_anthropic_context(chat_ctx.messages, id(self))
|
101
113
|
collaped_anthropic_ctx = _merge_messages(anthropic_ctx)
|
114
|
+
|
102
115
|
stream = self._client.messages.create(
|
103
|
-
max_tokens=opts.get("max_tokens",
|
116
|
+
max_tokens=opts.get("max_tokens", 1024),
|
104
117
|
system=latest_system_message,
|
105
118
|
messages=collaped_anthropic_ctx,
|
106
119
|
model=self._opts.model,
|
@@ -110,12 +123,15 @@ class LLM(llm.LLM):
|
|
110
123
|
**opts,
|
111
124
|
)
|
112
125
|
|
113
|
-
return LLMStream(
|
126
|
+
return LLMStream(
|
127
|
+
self, anthropic_stream=stream, chat_ctx=chat_ctx, fnc_ctx=fnc_ctx
|
128
|
+
)
|
114
129
|
|
115
130
|
|
116
131
|
class LLMStream(llm.LLMStream):
|
117
132
|
def __init__(
|
118
133
|
self,
|
134
|
+
llm: LLM,
|
119
135
|
*,
|
120
136
|
anthropic_stream: Awaitable[
|
121
137
|
anthropic.AsyncStream[anthropic.types.RawMessageStreamEvent]
|
@@ -123,7 +139,7 @@ class LLMStream(llm.LLMStream):
|
|
123
139
|
chat_ctx: llm.ChatContext,
|
124
140
|
fnc_ctx: llm.FunctionContext | None,
|
125
141
|
) -> None:
|
126
|
-
super().__init__(chat_ctx=chat_ctx, fnc_ctx=fnc_ctx)
|
142
|
+
super().__init__(llm, chat_ctx=chat_ctx, fnc_ctx=fnc_ctx)
|
127
143
|
self._awaitable_anthropic_stream = anthropic_stream
|
128
144
|
self._anthropic_stream: (
|
129
145
|
anthropic.AsyncStream[anthropic.types.RawMessageStreamEvent] | None
|
@@ -134,89 +150,113 @@ class LLMStream(llm.LLMStream):
|
|
134
150
|
self._fnc_name: str | None = None
|
135
151
|
self._fnc_raw_arguments: str | None = None
|
136
152
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
self._fnc_name = event.content_block.name
|
162
|
-
elif event.type == "content_block_delta":
|
163
|
-
delta = event.delta
|
164
|
-
if delta.type == "text_delta":
|
165
|
-
text = delta.text
|
166
|
-
|
167
|
-
# Anthropic seems to add a prompt when tool calling is enabled
|
168
|
-
# where responses always start with a "<thinking>" block containing
|
169
|
-
# the LLM's chain of thought. It's very verbose and not useful for voice
|
170
|
-
# applications.
|
171
|
-
if fn_calling_enabled:
|
172
|
-
if text.startswith("<thinking>"):
|
173
|
-
ignore = True
|
174
|
-
|
175
|
-
if "</thinking>" in text:
|
176
|
-
text = text.split("</thinking>")[-1]
|
177
|
-
ignore = False
|
178
|
-
|
179
|
-
if ignore:
|
180
|
-
continue
|
181
|
-
|
182
|
-
return llm.ChatChunk(
|
183
|
-
choices=[
|
184
|
-
llm.Choice(
|
185
|
-
delta=llm.ChoiceDelta(content=text, role="assistant")
|
186
|
-
)
|
187
|
-
]
|
188
|
-
)
|
189
|
-
elif delta.type == "input_json_delta":
|
190
|
-
assert self._fnc_raw_arguments is not None
|
191
|
-
self._fnc_raw_arguments += delta.partial_json
|
192
|
-
|
193
|
-
elif event.type == "content_block_stop":
|
194
|
-
if self._tool_call_id is not None and self._fnc_ctx:
|
195
|
-
assert self._fnc_name is not None
|
196
|
-
assert self._fnc_raw_arguments is not None
|
197
|
-
fnc_info = _create_ai_function_info(
|
198
|
-
self._fnc_ctx,
|
199
|
-
self._tool_call_id,
|
200
|
-
self._fnc_name,
|
201
|
-
self._fnc_raw_arguments,
|
153
|
+
self._request_id: str = ""
|
154
|
+
self._ignoring_cot = False # ignore chain of thought
|
155
|
+
self._input_tokens = 0
|
156
|
+
self._output_tokens = 0
|
157
|
+
|
158
|
+
async def _main_task(self) -> None:
|
159
|
+
try:
|
160
|
+
if not self._anthropic_stream:
|
161
|
+
self._anthropic_stream = await self._awaitable_anthropic_stream
|
162
|
+
|
163
|
+
async with self._anthropic_stream as stream:
|
164
|
+
async for event in stream:
|
165
|
+
chat_chunk = self._parse_event(event)
|
166
|
+
if chat_chunk is not None:
|
167
|
+
self._event_ch.send_nowait(chat_chunk)
|
168
|
+
|
169
|
+
self._event_ch.send_nowait(
|
170
|
+
llm.ChatChunk(
|
171
|
+
request_id=self._request_id,
|
172
|
+
usage=llm.CompletionUsage(
|
173
|
+
completion_tokens=self._output_tokens,
|
174
|
+
prompt_tokens=self._input_tokens,
|
175
|
+
total_tokens=self._input_tokens + self._output_tokens,
|
176
|
+
),
|
202
177
|
)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
178
|
+
)
|
179
|
+
except anthropic.APITimeoutError:
|
180
|
+
raise APITimeoutError()
|
181
|
+
except anthropic.APIStatusError as e:
|
182
|
+
raise APIStatusError(
|
183
|
+
e.message,
|
184
|
+
status_code=e.status_code,
|
185
|
+
request_id=e.request_id,
|
186
|
+
body=e.body,
|
187
|
+
)
|
188
|
+
except Exception as e:
|
189
|
+
raise APIConnectionError() from e
|
190
|
+
|
191
|
+
def _parse_event(
|
192
|
+
self, event: anthropic.types.RawMessageStreamEvent
|
193
|
+
) -> llm.ChatChunk | None:
|
194
|
+
if event.type == "message_start":
|
195
|
+
self._request_id = event.message.id
|
196
|
+
self._input_tokens = event.message.usage.input_tokens
|
197
|
+
self._output_tokens = event.message.usage.output_tokens
|
198
|
+
elif event.type == "message_delta":
|
199
|
+
self._output_tokens += event.usage.output_tokens
|
200
|
+
elif event.type == "content_block_start":
|
201
|
+
if event.content_block.type == "tool_use":
|
202
|
+
self._tool_call_id = event.content_block.id
|
203
|
+
self._fnc_name = event.content_block.name
|
204
|
+
self._fnc_raw_arguments = ""
|
205
|
+
elif event.type == "content_block_delta":
|
206
|
+
delta = event.delta
|
207
|
+
if delta.type == "text_delta":
|
208
|
+
text = delta.text
|
209
|
+
|
210
|
+
if self._fnc_ctx is not None:
|
211
|
+
# anthropic may inject COC when using functions
|
212
|
+
if text.startswith("<thinking>"):
|
213
|
+
self._ignoring_cot = True
|
214
|
+
elif self._ignoring_cot and "</thinking>" in text:
|
215
|
+
text = text.split("</thinking>")[-1]
|
216
|
+
self._ignoring_cot = False
|
217
|
+
|
218
|
+
if self._ignoring_cot:
|
219
|
+
return None
|
220
|
+
|
221
|
+
return llm.ChatChunk(
|
222
|
+
request_id=self._request_id,
|
223
|
+
choices=[
|
224
|
+
llm.Choice(
|
225
|
+
delta=llm.ChoiceDelta(content=text, role="assistant")
|
226
|
+
)
|
227
|
+
],
|
228
|
+
)
|
229
|
+
elif delta.type == "input_json_delta":
|
230
|
+
assert self._fnc_raw_arguments is not None
|
231
|
+
self._fnc_raw_arguments += delta.partial_json
|
232
|
+
|
233
|
+
elif event.type == "content_block_stop":
|
234
|
+
if self._tool_call_id is not None and self._fnc_ctx:
|
235
|
+
assert self._fnc_name is not None
|
236
|
+
assert self._fnc_raw_arguments is not None
|
237
|
+
|
238
|
+
fnc_info = _create_ai_function_info(
|
239
|
+
self._fnc_ctx,
|
240
|
+
self._tool_call_id,
|
241
|
+
self._fnc_name,
|
242
|
+
self._fnc_raw_arguments,
|
243
|
+
)
|
244
|
+
self._function_calls_info.append(fnc_info)
|
245
|
+
|
246
|
+
chat_chunk = llm.ChatChunk(
|
247
|
+
request_id=self._request_id,
|
248
|
+
choices=[
|
249
|
+
llm.Choice(
|
250
|
+
delta=llm.ChoiceDelta(
|
251
|
+
role="assistant", tool_calls=[fnc_info]
|
252
|
+
),
|
253
|
+
)
|
254
|
+
],
|
255
|
+
)
|
256
|
+
self._tool_call_id = self._fnc_raw_arguments = self._fnc_name = None
|
257
|
+
return chat_chunk
|
218
258
|
|
219
|
-
|
259
|
+
return None
|
220
260
|
|
221
261
|
|
222
262
|
def _latest_system_message(chat_ctx: llm.ChatContext) -> str:
|
@@ -286,7 +326,7 @@ def _build_anthropic_message(
|
|
286
326
|
a_content = a_msg["content"]
|
287
327
|
|
288
328
|
# add content if provided
|
289
|
-
if isinstance(msg.content, str):
|
329
|
+
if isinstance(msg.content, str) and msg.content:
|
290
330
|
a_msg["content"].append(
|
291
331
|
anthropic.types.TextBlock(
|
292
332
|
text=msg.content,
|
@@ -295,7 +335,7 @@ def _build_anthropic_message(
|
|
295
335
|
)
|
296
336
|
elif isinstance(msg.content, list):
|
297
337
|
for cnt in msg.content:
|
298
|
-
if isinstance(cnt, str):
|
338
|
+
if isinstance(cnt, str) and cnt:
|
299
339
|
content: anthropic.types.TextBlock = anthropic.types.TextBlock(
|
300
340
|
text=cnt,
|
301
341
|
type="text",
|
{livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.3.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.3
|
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,8 +19,8 @@ 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
|
23
|
-
Requires-Dist: anthropic
|
22
|
+
Requires-Dist: livekit-agents >=0.11
|
23
|
+
Requires-Dist: anthropic >=0.34
|
24
24
|
|
25
25
|
# LiveKit Plugins Anthropic
|
26
26
|
|
@@ -0,0 +1,10 @@
|
|
1
|
+
livekit/plugins/anthropic/__init__.py,sha256=1WCyNEaR6qBsX54qJQM0SeY-QHIucww16PLXcSnMqRo,1175
|
2
|
+
livekit/plugins/anthropic/llm.py,sha256=6rNtLJOfvB72gmmkFk8qN-N8fKnm-vhflpiBKTU_gqM,18921
|
3
|
+
livekit/plugins/anthropic/log.py,sha256=fG1pYSY88AnT738gZrmzF9FO4l4BdGENj3VKHMQB3Yo,72
|
4
|
+
livekit/plugins/anthropic/models.py,sha256=AVEhrEtKfWxsd-R03u7R74hcKjJq4oDVSTukvoPQGb0,179
|
5
|
+
livekit/plugins/anthropic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
livekit/plugins/anthropic/version.py,sha256=A5eBQ2sWvNCBt2aefaSvtvK0XRwtGa-rgF4340O8cjk,600
|
7
|
+
livekit_plugins_anthropic-0.2.3.dist-info/METADATA,sha256=dttEt-3lS_LDVl0XZkgnINXr560oPngIyXyzeJvih6c,1265
|
8
|
+
livekit_plugins_anthropic-0.2.3.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
9
|
+
livekit_plugins_anthropic-0.2.3.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
|
10
|
+
livekit_plugins_anthropic-0.2.3.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
livekit/plugins/anthropic/__init__.py,sha256=g6KUqOfZo9DIBwBD98u6QOWY7pr8ZYJJ61fk3AWpoa4,1006
|
2
|
-
livekit/plugins/anthropic/llm.py,sha256=s6Xt_5kuvypG1ZLUq_aYaYO_ZaTgagyM4b1c5iI2WUo,17652
|
3
|
-
livekit/plugins/anthropic/log.py,sha256=fG1pYSY88AnT738gZrmzF9FO4l4BdGENj3VKHMQB3Yo,72
|
4
|
-
livekit/plugins/anthropic/models.py,sha256=AVEhrEtKfWxsd-R03u7R74hcKjJq4oDVSTukvoPQGb0,179
|
5
|
-
livekit/plugins/anthropic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
livekit/plugins/anthropic/version.py,sha256=tb-eucPKT0bz3vQDaI1ltUZEhGKRFOFhN3U5ZIv_-xY,600
|
7
|
-
livekit_plugins_anthropic-0.2.1.dist-info/METADATA,sha256=-m1Q9gn3H1gRoVjFb1uIDfyJ5TP6p1HEMrh-zDyGeQs,1264
|
8
|
-
livekit_plugins_anthropic-0.2.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
9
|
-
livekit_plugins_anthropic-0.2.1.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
|
10
|
-
livekit_plugins_anthropic-0.2.1.dist-info/RECORD,,
|
File without changes
|