langroid 0.18.3__py3-none-any.whl → 0.19.0__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.
- langroid/agent/base.py +5 -0
- langroid/agent/callbacks/chainlit.py +27 -0
- langroid/agent/chat_agent.py +5 -5
- langroid/language_models/base.py +6 -0
- langroid/language_models/openai_gpt.py +98 -9
- {langroid-0.18.3.dist-info → langroid-0.19.0.dist-info}/METADATA +1 -1
- {langroid-0.18.3.dist-info → langroid-0.19.0.dist-info}/RECORD +10 -10
- pyproject.toml +1 -1
- {langroid-0.18.3.dist-info → langroid-0.19.0.dist-info}/LICENSE +0 -0
- {langroid-0.18.3.dist-info → langroid-0.19.0.dist-info}/WHEEL +0 -0
langroid/agent/base.py
CHANGED
@@ -108,6 +108,10 @@ def noop_fn(*args: List[Any], **kwargs: Dict[str, Any]) -> None:
|
|
108
108
|
pass
|
109
109
|
|
110
110
|
|
111
|
+
async def async_noop_fn(*args: List[Any], **kwargs: Dict[str, Any]) -> None:
|
112
|
+
pass
|
113
|
+
|
114
|
+
|
111
115
|
class Agent(ABC):
|
112
116
|
"""
|
113
117
|
An Agent is an abstraction that encapsulates mainly two components:
|
@@ -154,6 +158,7 @@ class Agent(ABC):
|
|
154
158
|
|
155
159
|
self.callbacks = SimpleNamespace(
|
156
160
|
start_llm_stream=lambda: noop_fn,
|
161
|
+
start_llm_stream_async=async_noop_fn,
|
157
162
|
cancel_llm_stream=noop_fn,
|
158
163
|
finish_llm_stream=noop_fn,
|
159
164
|
show_llm_response=noop_fn,
|
@@ -234,6 +234,7 @@ class ChainlitAgentCallbacks:
|
|
234
234
|
so we can alter the display of the first user message.
|
235
235
|
"""
|
236
236
|
agent.callbacks.start_llm_stream = self.start_llm_stream
|
237
|
+
agent.callbacks.start_llm_stream_async = self.start_llm_stream_async
|
237
238
|
agent.callbacks.cancel_llm_stream = self.cancel_llm_stream
|
238
239
|
agent.callbacks.finish_llm_stream = self.finish_llm_stream
|
239
240
|
agent.callbacks.show_llm_response = self.show_llm_response
|
@@ -304,6 +305,32 @@ class ChainlitAgentCallbacks:
|
|
304
305
|
|
305
306
|
return stream_token
|
306
307
|
|
308
|
+
async def start_llm_stream_async(self) -> Callable[[str], None]:
|
309
|
+
"""Returns a streaming fn that can be passed to the LLM class"""
|
310
|
+
self.stream = cl.Step(
|
311
|
+
id=self.curr_step.id if self.curr_step is not None else None,
|
312
|
+
name=self._entity_name("llm"),
|
313
|
+
type="llm",
|
314
|
+
parent_id=self._get_parent_id(),
|
315
|
+
)
|
316
|
+
self.last_step = self.stream
|
317
|
+
self.curr_step = None
|
318
|
+
logger.info(
|
319
|
+
f"""
|
320
|
+
Starting LLM stream for {self.agent.config.name}
|
321
|
+
id = {self.stream.id}
|
322
|
+
under parent {self._get_parent_id()}
|
323
|
+
"""
|
324
|
+
)
|
325
|
+
await self.stream.send() # type: ignore
|
326
|
+
|
327
|
+
async def stream_token(t: str) -> None:
|
328
|
+
if self.stream is None:
|
329
|
+
raise ValueError("Stream not initialized")
|
330
|
+
await self.stream.stream_token(t)
|
331
|
+
|
332
|
+
return stream_token
|
333
|
+
|
307
334
|
def cancel_llm_stream(self) -> None:
|
308
335
|
"""Called when cached response found."""
|
309
336
|
self.last_step = None
|
langroid/agent/chat_agent.py
CHANGED
@@ -9,7 +9,7 @@ from rich import print
|
|
9
9
|
from rich.console import Console
|
10
10
|
from rich.markup import escape
|
11
11
|
|
12
|
-
from langroid.agent.base import Agent, AgentConfig, noop_fn
|
12
|
+
from langroid.agent.base import Agent, AgentConfig, async_noop_fn, noop_fn
|
13
13
|
from langroid.agent.chat_document import ChatDocument
|
14
14
|
from langroid.agent.tool_message import ToolMessage
|
15
15
|
from langroid.agent.xml_tool_message import XMLToolMessage
|
@@ -969,10 +969,10 @@ class ChatAgent(Agent):
|
|
969
969
|
functions, fun_call, tools, force_tool = self._function_args()
|
970
970
|
assert self.llm is not None
|
971
971
|
|
972
|
-
|
972
|
+
streamer_async = async_noop_fn
|
973
973
|
if self.llm.get_stream():
|
974
|
-
|
975
|
-
self.llm.config.
|
974
|
+
streamer_async = await self.callbacks.start_llm_stream_async()
|
975
|
+
self.llm.config.streamer_async = streamer_async
|
976
976
|
|
977
977
|
response = await self.llm.achat(
|
978
978
|
messages,
|
@@ -989,7 +989,7 @@ class ChatAgent(Agent):
|
|
989
989
|
ChatDocument.from_LLMResponse(response, displayed=True),
|
990
990
|
),
|
991
991
|
)
|
992
|
-
self.llm.config.
|
992
|
+
self.llm.config.streamer_async = async_noop_fn
|
993
993
|
if response.cached:
|
994
994
|
self.callbacks.cancel_llm_stream()
|
995
995
|
self._render_llm_response(response)
|
langroid/language_models/base.py
CHANGED
@@ -6,6 +6,7 @@ from datetime import datetime
|
|
6
6
|
from enum import Enum
|
7
7
|
from typing import (
|
8
8
|
Any,
|
9
|
+
Awaitable,
|
9
10
|
Callable,
|
10
11
|
Dict,
|
11
12
|
List,
|
@@ -33,6 +34,10 @@ def noop_fn(*args: List[Any], **kwargs: Dict[str, Any]) -> None:
|
|
33
34
|
pass
|
34
35
|
|
35
36
|
|
37
|
+
async def async_noop_fn(*args: List[Any], **kwargs: Dict[str, Any]) -> None:
|
38
|
+
pass
|
39
|
+
|
40
|
+
|
36
41
|
FunctionCallTypes = Literal["none", "auto"]
|
37
42
|
ToolChoiceTypes = Literal["none", "auto", "required"]
|
38
43
|
ToolTypes = Literal["function"]
|
@@ -45,6 +50,7 @@ class LLMConfig(BaseSettings):
|
|
45
50
|
|
46
51
|
type: str = "openai"
|
47
52
|
streamer: Optional[Callable[[Any], None]] = noop_fn
|
53
|
+
streamer_async: Optional[Callable[..., Awaitable[None]]] = async_noop_fn
|
48
54
|
api_base: str | None = None
|
49
55
|
formatter: None | str = None
|
50
56
|
timeout: int = 20 # timeout for API requests
|
@@ -688,7 +688,6 @@ class OpenAIGPT(LanguageModel):
|
|
688
688
|
completion: str = "",
|
689
689
|
function_args: str = "",
|
690
690
|
function_name: str = "",
|
691
|
-
is_async: bool = False,
|
692
691
|
) -> Tuple[bool, bool, str, str]:
|
693
692
|
"""Process state vars while processing a streaming API response.
|
694
693
|
Returns a tuple consisting of:
|
@@ -708,7 +707,98 @@ class OpenAIGPT(LanguageModel):
|
|
708
707
|
event_args = ""
|
709
708
|
event_fn_name = ""
|
710
709
|
event_tool_deltas: Optional[List[Dict[str, Any]]] = None
|
711
|
-
|
710
|
+
# The first two events in the stream of Azure OpenAI is useless.
|
711
|
+
# In the 1st: choices list is empty, in the 2nd: the dict delta has null content
|
712
|
+
if chat:
|
713
|
+
delta = choices[0].get("delta", {})
|
714
|
+
event_text = delta.get("content", "")
|
715
|
+
if "function_call" in delta and delta["function_call"] is not None:
|
716
|
+
if "name" in delta["function_call"]:
|
717
|
+
event_fn_name = delta["function_call"]["name"]
|
718
|
+
if "arguments" in delta["function_call"]:
|
719
|
+
event_args = delta["function_call"]["arguments"]
|
720
|
+
if "tool_calls" in delta and delta["tool_calls"] is not None:
|
721
|
+
# it's a list of deltas, usually just one
|
722
|
+
event_tool_deltas = delta["tool_calls"]
|
723
|
+
tool_deltas += event_tool_deltas
|
724
|
+
else:
|
725
|
+
event_text = choices[0]["text"]
|
726
|
+
if event_text:
|
727
|
+
completion += event_text
|
728
|
+
sys.stdout.write(Colors().GREEN + event_text)
|
729
|
+
sys.stdout.flush()
|
730
|
+
self.config.streamer(event_text)
|
731
|
+
if event_fn_name:
|
732
|
+
function_name = event_fn_name
|
733
|
+
has_function = True
|
734
|
+
sys.stdout.write(Colors().GREEN + "FUNC: " + event_fn_name + ": ")
|
735
|
+
sys.stdout.flush()
|
736
|
+
self.config.streamer(event_fn_name)
|
737
|
+
|
738
|
+
if event_args:
|
739
|
+
function_args += event_args
|
740
|
+
sys.stdout.write(Colors().GREEN + event_args)
|
741
|
+
sys.stdout.flush()
|
742
|
+
self.config.streamer(event_args)
|
743
|
+
|
744
|
+
if event_tool_deltas is not None:
|
745
|
+
# print out streaming tool calls, if not async
|
746
|
+
for td in event_tool_deltas:
|
747
|
+
if td["function"]["name"] is not None:
|
748
|
+
tool_fn_name = td["function"]["name"]
|
749
|
+
sys.stdout.write(
|
750
|
+
Colors().GREEN + "OAI-TOOL: " + tool_fn_name + ": "
|
751
|
+
)
|
752
|
+
sys.stdout.flush()
|
753
|
+
self.config.streamer(tool_fn_name)
|
754
|
+
if td["function"]["arguments"] != "":
|
755
|
+
tool_fn_args = td["function"]["arguments"]
|
756
|
+
sys.stdout.write(Colors().GREEN + tool_fn_args)
|
757
|
+
sys.stdout.flush()
|
758
|
+
self.config.streamer(tool_fn_args)
|
759
|
+
|
760
|
+
# show this delta in the stream
|
761
|
+
if choices[0].get("finish_reason", "") in [
|
762
|
+
"stop",
|
763
|
+
"function_call",
|
764
|
+
"tool_calls",
|
765
|
+
]:
|
766
|
+
# for function_call, finish_reason does not necessarily
|
767
|
+
# contain "function_call" as mentioned in the docs.
|
768
|
+
# So we check for "stop" or "function_call" here.
|
769
|
+
return True, has_function, function_name, function_args, completion
|
770
|
+
return False, has_function, function_name, function_args, completion
|
771
|
+
|
772
|
+
@no_type_check
|
773
|
+
async def _process_stream_event_async(
|
774
|
+
self,
|
775
|
+
event,
|
776
|
+
chat: bool = False,
|
777
|
+
tool_deltas: List[Dict[str, Any]] = [],
|
778
|
+
has_function: bool = False,
|
779
|
+
completion: str = "",
|
780
|
+
function_args: str = "",
|
781
|
+
function_name: str = "",
|
782
|
+
) -> Tuple[bool, bool, str, str]:
|
783
|
+
"""Process state vars while processing a streaming API response.
|
784
|
+
Returns a tuple consisting of:
|
785
|
+
- is_break: whether to break out of the loop
|
786
|
+
- has_function: whether the response contains a function_call
|
787
|
+
- function_name: name of the function
|
788
|
+
- function_args: args of the function
|
789
|
+
"""
|
790
|
+
# convert event obj (of type ChatCompletionChunk) to dict so rest of code,
|
791
|
+
# which expects dicts, works as it did before switching to openai v1.x
|
792
|
+
if not isinstance(event, dict):
|
793
|
+
event = event.model_dump()
|
794
|
+
|
795
|
+
choices = event.get("choices", [{}])
|
796
|
+
if len(choices) == 0:
|
797
|
+
choices = [{}]
|
798
|
+
event_args = ""
|
799
|
+
event_fn_name = ""
|
800
|
+
event_tool_deltas: Optional[List[Dict[str, Any]]] = None
|
801
|
+
silent = self.config.async_stream_quiet
|
712
802
|
# The first two events in the stream of Azure OpenAI is useless.
|
713
803
|
# In the 1st: choices list is empty, in the 2nd: the dict delta has null content
|
714
804
|
if chat:
|
@@ -730,21 +820,21 @@ class OpenAIGPT(LanguageModel):
|
|
730
820
|
if not silent:
|
731
821
|
sys.stdout.write(Colors().GREEN + event_text)
|
732
822
|
sys.stdout.flush()
|
733
|
-
self.config.
|
823
|
+
await self.config.streamer_async(event_text)
|
734
824
|
if event_fn_name:
|
735
825
|
function_name = event_fn_name
|
736
826
|
has_function = True
|
737
827
|
if not silent:
|
738
828
|
sys.stdout.write(Colors().GREEN + "FUNC: " + event_fn_name + ": ")
|
739
829
|
sys.stdout.flush()
|
740
|
-
self.config.
|
830
|
+
await self.config.streamer_async(event_fn_name)
|
741
831
|
|
742
832
|
if event_args:
|
743
833
|
function_args += event_args
|
744
834
|
if not silent:
|
745
835
|
sys.stdout.write(Colors().GREEN + event_args)
|
746
836
|
sys.stdout.flush()
|
747
|
-
self.config.
|
837
|
+
await self.config.streamer_async(event_args)
|
748
838
|
|
749
839
|
if event_tool_deltas is not None and not silent:
|
750
840
|
# print out streaming tool calls, if not async
|
@@ -755,12 +845,12 @@ class OpenAIGPT(LanguageModel):
|
|
755
845
|
Colors().GREEN + "OAI-TOOL: " + tool_fn_name + ": "
|
756
846
|
)
|
757
847
|
sys.stdout.flush()
|
758
|
-
self.config.
|
848
|
+
await self.config.streamer_async(tool_fn_name)
|
759
849
|
if td["function"]["arguments"] != "":
|
760
850
|
tool_fn_args = td["function"]["arguments"]
|
761
851
|
sys.stdout.write(Colors().GREEN + tool_fn_args)
|
762
852
|
sys.stdout.flush()
|
763
|
-
self.config.
|
853
|
+
await self.config.streamer_async(tool_fn_args)
|
764
854
|
|
765
855
|
# show this delta in the stream
|
766
856
|
if choices[0].get("finish_reason", "") in [
|
@@ -862,7 +952,7 @@ class OpenAIGPT(LanguageModel):
|
|
862
952
|
function_name,
|
863
953
|
function_args,
|
864
954
|
completion,
|
865
|
-
) = self.
|
955
|
+
) = await self._process_stream_event_async(
|
866
956
|
event,
|
867
957
|
chat=chat,
|
868
958
|
tool_deltas=tool_deltas,
|
@@ -870,7 +960,6 @@ class OpenAIGPT(LanguageModel):
|
|
870
960
|
completion=completion,
|
871
961
|
function_args=function_args,
|
872
962
|
function_name=function_name,
|
873
|
-
is_async=True,
|
874
963
|
)
|
875
964
|
if is_break:
|
876
965
|
break
|
@@ -1,10 +1,10 @@
|
|
1
1
|
langroid/__init__.py,sha256=z_fCOLQJPOw3LLRPBlFB5-2HyCjpPgQa4m4iY5Fvb8Y,1800
|
2
2
|
langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
|
3
|
-
langroid/agent/base.py,sha256=
|
3
|
+
langroid/agent/base.py,sha256=bk_N8J7yKdkuopM8z86MkngAfHt5z0qh1HWdcI4tbPw,64420
|
4
4
|
langroid/agent/batch.py,sha256=QZdlt1563hx4l3AXrCaGovE-PNG93M3DsvQAbDzdiS8,13705
|
5
5
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
langroid/agent/callbacks/chainlit.py,sha256=
|
7
|
-
langroid/agent/chat_agent.py,sha256=
|
6
|
+
langroid/agent/callbacks/chainlit.py,sha256=JJXI3UGTyTDg2FFath4rqY1GyUo_0pbVBt8CZpvdtn4,23289
|
7
|
+
langroid/agent/chat_agent.py,sha256=GVuKXAwHACeTc1gC0y7Ywj8CmJfqu7InJ0AXtmz8nRw,50000
|
8
8
|
langroid/agent/chat_document.py,sha256=FZ_PkeKU5OVp1IUlMvspfqxIXzlyd7J_F32DSYrxQ7E,17651
|
9
9
|
langroid/agent/helpers.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
langroid/agent/junk,sha256=LxfuuW7Cijsg0szAzT81OjWWv1PMNI-6w_-DspVIO2s,339
|
@@ -67,10 +67,10 @@ langroid/language_models/.chainlit/config.toml,sha256=1t5lHORGzc2E6dkaO9P15jYHu2
|
|
67
67
|
langroid/language_models/.chainlit/translations/en-US.json,sha256=DAFz2HjOFFfboCStrUfKFg2BpplJPK_OOtixwF_GivY,9931
|
68
68
|
langroid/language_models/__init__.py,sha256=1sUGobooTqq77XC7LxKsvME0RgSd5GGmeyrPo9SMh4U,940
|
69
69
|
langroid/language_models/azure_openai.py,sha256=G4le3j4YLHV7IwgB2C37hO3MKijZ1KjynbYlEvpIF7Y,6214
|
70
|
-
langroid/language_models/base.py,sha256=
|
70
|
+
langroid/language_models/base.py,sha256=xMFg8syIHiA7ABRNWPXeFM1vGeY_1EN84ki8C3dycfw,22722
|
71
71
|
langroid/language_models/config.py,sha256=9Q8wk5a7RQr8LGMT_0WkpjY8S4ywK06SalVRjXlfCiI,378
|
72
72
|
langroid/language_models/mock_lm.py,sha256=HuiAvjHiCfffYF5xjFJUq945HVTW0QPbeUUctOnNCzQ,3868
|
73
|
-
langroid/language_models/openai_gpt.py,sha256=
|
73
|
+
langroid/language_models/openai_gpt.py,sha256=MwhdyF5RNfTupGL__jU1rx0LJQe2UvoKnnK4vTBHWDs,68799
|
74
74
|
langroid/language_models/prompt_formatter/__init__.py,sha256=2-5cdE24XoFDhifOLl8yiscohil1ogbP1ECkYdBlBsk,372
|
75
75
|
langroid/language_models/prompt_formatter/base.py,sha256=eDS1sgRNZVnoajwV_ZIha6cba5Dt8xjgzdRbPITwx3Q,1221
|
76
76
|
langroid/language_models/prompt_formatter/hf_formatter.py,sha256=PVJppmjRvD-2DF-XNC6mE05vTZ9wbu37SmXwZBQhad0,5055
|
@@ -137,8 +137,8 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
|
|
137
137
|
langroid/vector_store/momento.py,sha256=qR-zBF1RKVHQZPZQYW_7g-XpTwr46p8HJuYPCkfJbM4,10534
|
138
138
|
langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
|
139
139
|
langroid/vector_store/qdrantdb.py,sha256=v88lqFkepADvlN6lByUj9I4NEKa9X9lWH16uTPPbYrE,17457
|
140
|
-
pyproject.toml,sha256=
|
141
|
-
langroid-0.
|
142
|
-
langroid-0.
|
143
|
-
langroid-0.
|
144
|
-
langroid-0.
|
140
|
+
pyproject.toml,sha256=XuRvlqpTV_ElysTBe0HFQQ18fa8kQ0PxA4Rb-LqQzZY,7251
|
141
|
+
langroid-0.19.0.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
142
|
+
langroid-0.19.0.dist-info/METADATA,sha256=SVEU3MdLLeCzg4dzcVt9vrAIzujlCSTM3b9dosHuBug,56484
|
143
|
+
langroid-0.19.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
144
|
+
langroid-0.19.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|