livekit-plugins-anthropic 0.2.1__py3-none-any.whl → 0.2.2__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 +121 -82
- livekit/plugins/anthropic/version.py +1 -1
- {livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.2.dist-info}/METADATA +3 -3
- livekit_plugins_anthropic-0.2.2.dist-info/RECORD +10 -0
- {livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.2.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.2.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] = []
|
@@ -100,7 +112,7 @@ class LLM(llm.LLM):
|
|
100
112
|
anthropic_ctx = _build_anthropic_context(chat_ctx.messages, id(self))
|
101
113
|
collaped_anthropic_ctx = _merge_messages(anthropic_ctx)
|
102
114
|
stream = self._client.messages.create(
|
103
|
-
max_tokens=opts.get("max_tokens",
|
115
|
+
max_tokens=opts.get("max_tokens", 1024),
|
104
116
|
system=latest_system_message,
|
105
117
|
messages=collaped_anthropic_ctx,
|
106
118
|
model=self._opts.model,
|
@@ -110,12 +122,15 @@ class LLM(llm.LLM):
|
|
110
122
|
**opts,
|
111
123
|
)
|
112
124
|
|
113
|
-
return LLMStream(
|
125
|
+
return LLMStream(
|
126
|
+
self, anthropic_stream=stream, chat_ctx=chat_ctx, fnc_ctx=fnc_ctx
|
127
|
+
)
|
114
128
|
|
115
129
|
|
116
130
|
class LLMStream(llm.LLMStream):
|
117
131
|
def __init__(
|
118
132
|
self,
|
133
|
+
llm: LLM,
|
119
134
|
*,
|
120
135
|
anthropic_stream: Awaitable[
|
121
136
|
anthropic.AsyncStream[anthropic.types.RawMessageStreamEvent]
|
@@ -123,7 +138,7 @@ class LLMStream(llm.LLMStream):
|
|
123
138
|
chat_ctx: llm.ChatContext,
|
124
139
|
fnc_ctx: llm.FunctionContext | None,
|
125
140
|
) -> None:
|
126
|
-
super().__init__(chat_ctx=chat_ctx, fnc_ctx=fnc_ctx)
|
141
|
+
super().__init__(llm, chat_ctx=chat_ctx, fnc_ctx=fnc_ctx)
|
127
142
|
self._awaitable_anthropic_stream = anthropic_stream
|
128
143
|
self._anthropic_stream: (
|
129
144
|
anthropic.AsyncStream[anthropic.types.RawMessageStreamEvent] | None
|
@@ -134,89 +149,113 @@ class LLMStream(llm.LLMStream):
|
|
134
149
|
self._fnc_name: str | None = None
|
135
150
|
self._fnc_raw_arguments: str | None = None
|
136
151
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
return await super().aclose()
|
152
|
+
self._request_id: str = ""
|
153
|
+
self._ignoring_cot = False # ignore chain of thought
|
154
|
+
self._input_tokens = 0
|
155
|
+
self._output_tokens = 0
|
142
156
|
|
143
|
-
async def
|
157
|
+
async def _main_task(self) -> None:
|
144
158
|
if not self._anthropic_stream:
|
145
159
|
self._anthropic_stream = await self._awaitable_anthropic_stream
|
146
160
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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,
|
161
|
+
try:
|
162
|
+
async with self._anthropic_stream as stream:
|
163
|
+
async for event in stream:
|
164
|
+
chat_chunk = self._parse_event(event)
|
165
|
+
if chat_chunk is not None:
|
166
|
+
self._event_ch.send_nowait(chat_chunk)
|
167
|
+
|
168
|
+
self._event_ch.send_nowait(
|
169
|
+
llm.ChatChunk(
|
170
|
+
request_id=self._request_id,
|
171
|
+
usage=llm.CompletionUsage(
|
172
|
+
completion_tokens=self._output_tokens,
|
173
|
+
prompt_tokens=self._input_tokens,
|
174
|
+
total_tokens=self._input_tokens + self._output_tokens,
|
175
|
+
),
|
202
176
|
)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
177
|
+
)
|
178
|
+
except anthropic.APITimeoutError:
|
179
|
+
raise APITimeoutError()
|
180
|
+
except anthropic.APIStatusError as e:
|
181
|
+
raise APIStatusError(
|
182
|
+
e.message,
|
183
|
+
status_code=e.status_code,
|
184
|
+
request_id=e.request_id,
|
185
|
+
body=e.body,
|
186
|
+
)
|
187
|
+
except Exception as e:
|
188
|
+
raise APIConnectionError() from e
|
189
|
+
|
190
|
+
def _parse_event(
|
191
|
+
self, event: anthropic.types.RawMessageStreamEvent
|
192
|
+
) -> llm.ChatChunk | None:
|
193
|
+
if event.type == "message_start":
|
194
|
+
self._request_id = event.message.id
|
195
|
+
self._input_tokens = event.message.usage.input_tokens
|
196
|
+
self._output_tokens = event.message.usage.output_tokens
|
197
|
+
elif event.type == "message_delta":
|
198
|
+
self._output_tokens += event.usage.output_tokens
|
199
|
+
elif event.type == "content_block_start":
|
200
|
+
if event.content_block.type == "tool_use":
|
201
|
+
self._tool_call_id = event.content_block.id
|
202
|
+
self._fnc_name = event.content_block.name
|
203
|
+
self._fnc_raw_arguments = ""
|
204
|
+
elif event.type == "content_block_delta":
|
205
|
+
delta = event.delta
|
206
|
+
if delta.type == "text_delta":
|
207
|
+
text = delta.text
|
208
|
+
|
209
|
+
if self._fnc_ctx is not None:
|
210
|
+
# anthropic may inject COC when using functions
|
211
|
+
if text.startswith("<thinking>"):
|
212
|
+
self._ignoring_cot = True
|
213
|
+
elif self._ignoring_cot and "</thinking>" in text:
|
214
|
+
text = text.split("</thinking>")[-1]
|
215
|
+
self._ignoring_cot = False
|
216
|
+
|
217
|
+
if self._ignoring_cot:
|
218
|
+
return None
|
219
|
+
|
220
|
+
return llm.ChatChunk(
|
221
|
+
request_id=self._request_id,
|
222
|
+
choices=[
|
223
|
+
llm.Choice(
|
224
|
+
delta=llm.ChoiceDelta(content=text, role="assistant")
|
225
|
+
)
|
226
|
+
],
|
227
|
+
)
|
228
|
+
elif delta.type == "input_json_delta":
|
229
|
+
assert self._fnc_raw_arguments is not None
|
230
|
+
self._fnc_raw_arguments += delta.partial_json
|
231
|
+
|
232
|
+
elif event.type == "content_block_stop":
|
233
|
+
if self._tool_call_id is not None and self._fnc_ctx:
|
234
|
+
assert self._fnc_name is not None
|
235
|
+
assert self._fnc_raw_arguments is not None
|
236
|
+
|
237
|
+
fnc_info = _create_ai_function_info(
|
238
|
+
self._fnc_ctx,
|
239
|
+
self._tool_call_id,
|
240
|
+
self._fnc_name,
|
241
|
+
self._fnc_raw_arguments,
|
242
|
+
)
|
243
|
+
self._function_calls_info.append(fnc_info)
|
244
|
+
|
245
|
+
chat_chunk = llm.ChatChunk(
|
246
|
+
request_id=self._request_id,
|
247
|
+
choices=[
|
248
|
+
llm.Choice(
|
249
|
+
delta=llm.ChoiceDelta(
|
250
|
+
role="assistant", tool_calls=[fnc_info]
|
251
|
+
),
|
252
|
+
)
|
253
|
+
],
|
254
|
+
)
|
255
|
+
self._tool_call_id = self._fnc_raw_arguments = self._fnc_name = None
|
256
|
+
return chat_chunk
|
218
257
|
|
219
|
-
|
258
|
+
return None
|
220
259
|
|
221
260
|
|
222
261
|
def _latest_system_message(chat_ctx: llm.ChatContext) -> str:
|
{livekit_plugins_anthropic-0.2.1.dist-info → livekit_plugins_anthropic-0.2.2.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.2
|
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=mcTBYT3_ZVAWx9ZnCUj_96NM44dF6SF1R0ZLMUQt79Y,18888
|
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=vseqf_ctDD3TBGPSArXT_dFZvNHkuJc4_8GgQSvrKrM,600
|
7
|
+
livekit_plugins_anthropic-0.2.2.dist-info/METADATA,sha256=KWqm8V_Ooo_sGKJ2JNAVrfOCu1sfuSLLBHkOC4tZHfQ,1265
|
8
|
+
livekit_plugins_anthropic-0.2.2.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
9
|
+
livekit_plugins_anthropic-0.2.2.dist-info/top_level.txt,sha256=OoDok3xUmXbZRvOrfvvXB-Juu4DX79dlq188E19YHoo,8
|
10
|
+
livekit_plugins_anthropic-0.2.2.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
|