langroid 0.10.2__py3-none-any.whl → 0.12.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/__init__.py +1 -2
- langroid/agent/base.py +138 -54
- langroid/agent/batch.py +116 -28
- langroid/agent/chat_agent.py +25 -4
- langroid/agent/chat_document.py +5 -1
- langroid/agent/special/doc_chat_agent.py +2 -2
- langroid/agent/task.py +131 -26
- langroid/agent/tool_message.py +15 -43
- langroid/agent/tools/__init__.py +4 -0
- langroid/agent/tools/orchestration.py +87 -8
- langroid/language_models/mock_lm.py +28 -7
- langroid/parsing/web_search.py +7 -4
- langroid/utils/.chainlit/config.toml +121 -0
- langroid/utils/.chainlit/translations/en-US.json +231 -0
- langroid/utils/types.py +93 -0
- {langroid-0.10.2.dist-info → langroid-0.12.0.dist-info}/METADATA +4 -2
- {langroid-0.10.2.dist-info → langroid-0.12.0.dist-info}/RECORD +20 -17
- pyproject.toml +2 -2
- {langroid-0.10.2.dist-info → langroid-0.12.0.dist-info}/LICENSE +0 -0
- {langroid-0.10.2.dist-info → langroid-0.12.0.dist-info}/WHEEL +0 -0
@@ -3,12 +3,14 @@ Various tools to for agents to be able to control flow of Task, e.g.
|
|
3
3
|
termination, routing to another agent, etc.
|
4
4
|
"""
|
5
5
|
|
6
|
-
from typing import List, Tuple
|
6
|
+
from typing import Any, List, Tuple
|
7
7
|
|
8
8
|
from langroid.agent.chat_agent import ChatAgent
|
9
9
|
from langroid.agent.chat_document import ChatDocument
|
10
10
|
from langroid.agent.tool_message import ToolMessage
|
11
11
|
from langroid.mytypes import Entity
|
12
|
+
from langroid.pydantic_v1 import Extra
|
13
|
+
from langroid.utils.types import to_string
|
12
14
|
|
13
15
|
|
14
16
|
class AgentDoneTool(ToolMessage):
|
@@ -17,16 +19,20 @@ class AgentDoneTool(ToolMessage):
|
|
17
19
|
|
18
20
|
purpose: str = """
|
19
21
|
To signal the current task is done, along with an optional message <content>
|
20
|
-
(default
|
22
|
+
of arbitrary type (default None) and an
|
23
|
+
optional list of <tools> (default empty list).
|
21
24
|
"""
|
22
25
|
request: str = "agent_done_tool"
|
23
|
-
content:
|
26
|
+
content: Any = None
|
24
27
|
tools: List[ToolMessage] = []
|
25
|
-
|
28
|
+
# only meant for agent_response or tool-handlers, not for LLM generation:
|
29
|
+
_allow_llm_use: bool = False
|
26
30
|
|
27
31
|
def response(self, agent: ChatAgent) -> ChatDocument:
|
32
|
+
content_str = "" if self.content is None else to_string(self.content)
|
28
33
|
return agent.create_agent_response(
|
29
|
-
|
34
|
+
content=content_str,
|
35
|
+
content_any=self.content,
|
30
36
|
tool_messages=[self] + self.tools,
|
31
37
|
)
|
32
38
|
|
@@ -37,14 +43,15 @@ class DoneTool(ToolMessage):
|
|
37
43
|
|
38
44
|
purpose = """
|
39
45
|
To signal the current task is done, along with an optional message <content>
|
40
|
-
(default
|
46
|
+
of arbitrary type (default None).
|
41
47
|
"""
|
42
48
|
request = "done_tool"
|
43
49
|
content: str = ""
|
44
50
|
|
45
51
|
def response(self, agent: ChatAgent) -> ChatDocument:
|
46
52
|
return agent.create_agent_response(
|
47
|
-
self.content,
|
53
|
+
content=self.content,
|
54
|
+
content_any=self.content,
|
48
55
|
tool_messages=[self],
|
49
56
|
)
|
50
57
|
|
@@ -58,6 +65,78 @@ class DoneTool(ToolMessage):
|
|
58
65
|
"""
|
59
66
|
|
60
67
|
|
68
|
+
class ResultTool(ToolMessage):
|
69
|
+
"""Class to use as a wrapper for sending arbitrary results from an Agent's
|
70
|
+
agent_response or tool handlers, to:
|
71
|
+
(a) trigger completion of the current task (similar to (Agent)DoneTool), and
|
72
|
+
(b) be returned as the result of the current task, i.e. this tool would appear
|
73
|
+
in the resulting ChatDocument's `tool_messages` list.
|
74
|
+
See test_tool_handlers_and_results in test_tool_messages.py, and
|
75
|
+
examples/basic/tool-extract-short-example.py.
|
76
|
+
|
77
|
+
Note:
|
78
|
+
- when defining a tool handler or agent_response, you can directly return
|
79
|
+
ResultTool(field1 = val1, ...),
|
80
|
+
where the values can be aribitrary data structures, including nested
|
81
|
+
Pydantic objs, or you can define a subclass of ResultTool with the
|
82
|
+
fields you want to return.
|
83
|
+
- This is a special ToolMessage that is NOT meant to be used or handled
|
84
|
+
by an agent.
|
85
|
+
- AgentDoneTool is more restrictive in that you can only send a `content`
|
86
|
+
or `tools` in the result.
|
87
|
+
"""
|
88
|
+
|
89
|
+
request: str = "result_tool"
|
90
|
+
purpose: str = "Ignored; Wrapper for a structured message"
|
91
|
+
id: str = "" # placeholder for OpenAI-API tool_call_id
|
92
|
+
|
93
|
+
class Config:
|
94
|
+
extra = Extra.allow
|
95
|
+
arbitrary_types_allowed = False
|
96
|
+
validate_all = True
|
97
|
+
validate_assignment = True
|
98
|
+
# do not include these fields in the generated schema
|
99
|
+
# since we don't require the LLM to specify them
|
100
|
+
schema_extra = {"exclude": {"purpose", "id"}}
|
101
|
+
|
102
|
+
def handle(self) -> AgentDoneTool:
|
103
|
+
return AgentDoneTool(tools=[self])
|
104
|
+
|
105
|
+
|
106
|
+
class FinalResultTool(ToolMessage):
|
107
|
+
"""Class to use as a wrapper for sending arbitrary results from an Agent's
|
108
|
+
agent_response or tool handlers, to:
|
109
|
+
(a) trigger completion of the current task as well as all parent tasks, and
|
110
|
+
(b) be returned as the final result of the root task, i.e. this tool would appear
|
111
|
+
in the final ChatDocument's `tool_messages` list.
|
112
|
+
See test_tool_handlers_and_results in test_tool_messages.py, and
|
113
|
+
examples/basic/tool-extract-short-example.py.
|
114
|
+
|
115
|
+
Note:
|
116
|
+
- when defining a tool handler or agent_response, you can directly return
|
117
|
+
FinalResultTool(field1 = val1, ...),
|
118
|
+
where the values can be aribitrary data structures, including nested
|
119
|
+
Pydantic objs, or you can define a subclass of FinalResultTool with the
|
120
|
+
fields you want to return.
|
121
|
+
- This is a special ToolMessage that is NOT meant to be used or handled
|
122
|
+
by an agent.
|
123
|
+
"""
|
124
|
+
|
125
|
+
request: str = ""
|
126
|
+
purpose: str = "Ignored; Wrapper for a structured message"
|
127
|
+
id: str = "" # placeholder for OpenAI-API tool_call_id
|
128
|
+
_allow_llm_use: bool = False
|
129
|
+
|
130
|
+
class Config:
|
131
|
+
extra = Extra.allow
|
132
|
+
arbitrary_types_allowed = False
|
133
|
+
validate_all = True
|
134
|
+
validate_assignment = True
|
135
|
+
# do not include these fields in the generated schema
|
136
|
+
# since we don't require the LLM to specify them
|
137
|
+
schema_extra = {"exclude": {"purpose", "id"}}
|
138
|
+
|
139
|
+
|
61
140
|
class PassTool(ToolMessage):
|
62
141
|
"""Tool for "passing" on the received msg (ChatDocument),
|
63
142
|
so that an as-yet-unspecified agent can handle it.
|
@@ -206,7 +285,7 @@ class AgentSendTool(ToolMessage):
|
|
206
285
|
to: str
|
207
286
|
content: str = ""
|
208
287
|
tools: List[ToolMessage] = []
|
209
|
-
|
288
|
+
_allow_llm_use: bool = False
|
210
289
|
|
211
290
|
def response(self, agent: ChatAgent) -> ChatDocument:
|
212
291
|
return agent.create_agent_response(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Mock Language Model for testing"""
|
2
2
|
|
3
|
-
from typing import Callable, Dict, List, Optional, Union
|
3
|
+
from typing import Awaitable, Callable, Dict, List, Optional, Union
|
4
4
|
|
5
5
|
import langroid.language_models as lm
|
6
6
|
from langroid.language_models import LLMResponse
|
@@ -10,6 +10,7 @@ from langroid.language_models.base import (
|
|
10
10
|
OpenAIToolSpec,
|
11
11
|
ToolChoiceTypes,
|
12
12
|
)
|
13
|
+
from langroid.utils.types import to_string
|
13
14
|
|
14
15
|
|
15
16
|
def none_fn(x: str) -> None | str:
|
@@ -27,6 +28,7 @@ class MockLMConfig(LLMConfig):
|
|
27
28
|
|
28
29
|
response_dict: Dict[str, str] = {}
|
29
30
|
response_fn: Callable[[str], None | str] = none_fn
|
31
|
+
response_fn_async: Optional[Callable[[str], Awaitable[Optional[str]]]] = None
|
30
32
|
default_response: str = "Mock response"
|
31
33
|
|
32
34
|
type: str = "mock"
|
@@ -43,11 +45,30 @@ class MockLM(LanguageModel):
|
|
43
45
|
# - response_dict
|
44
46
|
# - response_fn
|
45
47
|
# - default_response
|
48
|
+
mapped_response = self.config.response_dict.get(
|
49
|
+
msg, self.config.response_fn(msg) or self.config.default_response
|
50
|
+
)
|
46
51
|
return lm.LLMResponse(
|
47
|
-
message=
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
message=to_string(mapped_response),
|
53
|
+
cached=False,
|
54
|
+
)
|
55
|
+
|
56
|
+
async def _response_async(self, msg: str) -> LLMResponse:
|
57
|
+
# response is based on this fallback order:
|
58
|
+
# - response_dict
|
59
|
+
# - response_fn_async
|
60
|
+
# - response_fn
|
61
|
+
# - default_response
|
62
|
+
if self.config.response_fn_async is not None:
|
63
|
+
response = await self.config.response_fn_async(msg)
|
64
|
+
else:
|
65
|
+
response = self.config.response_fn(msg)
|
66
|
+
|
67
|
+
mapped_response = self.config.response_dict.get(
|
68
|
+
msg, response or self.config.default_response
|
69
|
+
)
|
70
|
+
return lm.LLMResponse(
|
71
|
+
message=to_string(mapped_response),
|
51
72
|
cached=False,
|
52
73
|
)
|
53
74
|
|
@@ -79,7 +100,7 @@ class MockLM(LanguageModel):
|
|
79
100
|
Mock chat function for testing
|
80
101
|
"""
|
81
102
|
last_msg = messages[-1].content if isinstance(messages, list) else messages
|
82
|
-
return self.
|
103
|
+
return await self._response_async(last_msg)
|
83
104
|
|
84
105
|
def generate(self, prompt: str, max_tokens: int = 200) -> lm.LLMResponse:
|
85
106
|
"""
|
@@ -91,7 +112,7 @@ class MockLM(LanguageModel):
|
|
91
112
|
"""
|
92
113
|
Mock generate function for testing
|
93
114
|
"""
|
94
|
-
return self.
|
115
|
+
return await self._response_async(prompt)
|
95
116
|
|
96
117
|
def get_stream(self) -> bool:
|
97
118
|
return False
|
langroid/parsing/web_search.py
CHANGED
@@ -48,10 +48,13 @@ class WebSearchResult:
|
|
48
48
|
return self.full_content[: self.max_summary_length]
|
49
49
|
|
50
50
|
def get_full_content(self) -> str:
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
try:
|
52
|
+
response: Response = requests.get(self.link)
|
53
|
+
soup: BeautifulSoup = BeautifulSoup(response.text, "lxml")
|
54
|
+
text = " ".join(soup.stripped_strings)
|
55
|
+
return text[: self.max_content_length]
|
56
|
+
except Exception as e:
|
57
|
+
return f"Error fetching content from {self.link}: {e}"
|
55
58
|
|
56
59
|
def __str__(self) -> str:
|
57
60
|
return f"Title: {self.title}\nLink: {self.link}\nSummary: {self.summary}"
|
@@ -0,0 +1,121 @@
|
|
1
|
+
[project]
|
2
|
+
# Whether to enable telemetry (default: true). No personal data is collected.
|
3
|
+
enable_telemetry = true
|
4
|
+
|
5
|
+
|
6
|
+
# List of environment variables to be provided by each user to use the app.
|
7
|
+
user_env = []
|
8
|
+
|
9
|
+
# Duration (in seconds) during which the session is saved when the connection is lost
|
10
|
+
session_timeout = 3600
|
11
|
+
|
12
|
+
# Enable third parties caching (e.g LangChain cache)
|
13
|
+
cache = false
|
14
|
+
|
15
|
+
# Authorized origins
|
16
|
+
allow_origins = ["*"]
|
17
|
+
|
18
|
+
# Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
|
19
|
+
# follow_symlink = false
|
20
|
+
|
21
|
+
[features]
|
22
|
+
# Show the prompt playground
|
23
|
+
prompt_playground = true
|
24
|
+
|
25
|
+
# Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript)
|
26
|
+
unsafe_allow_html = false
|
27
|
+
|
28
|
+
# Process and display mathematical expressions. This can clash with "$" characters in messages.
|
29
|
+
latex = false
|
30
|
+
|
31
|
+
# Automatically tag threads with the current chat profile (if a chat profile is used)
|
32
|
+
auto_tag_thread = true
|
33
|
+
|
34
|
+
# Authorize users to spontaneously upload files with messages
|
35
|
+
[features.spontaneous_file_upload]
|
36
|
+
enabled = true
|
37
|
+
accept = ["*/*"]
|
38
|
+
max_files = 20
|
39
|
+
max_size_mb = 500
|
40
|
+
|
41
|
+
[features.audio]
|
42
|
+
# Threshold for audio recording
|
43
|
+
min_decibels = -45
|
44
|
+
# Delay for the user to start speaking in MS
|
45
|
+
initial_silence_timeout = 3000
|
46
|
+
# Delay for the user to continue speaking in MS. If the user stops speaking for this duration, the recording will stop.
|
47
|
+
silence_timeout = 1500
|
48
|
+
# Above this duration (MS), the recording will forcefully stop.
|
49
|
+
max_duration = 15000
|
50
|
+
# Duration of the audio chunks in MS
|
51
|
+
chunk_duration = 1000
|
52
|
+
# Sample rate of the audio
|
53
|
+
sample_rate = 44100
|
54
|
+
|
55
|
+
[UI]
|
56
|
+
# Name of the app and chatbot.
|
57
|
+
name = "Chatbot"
|
58
|
+
|
59
|
+
# Show the readme while the thread is empty.
|
60
|
+
show_readme_as_default = true
|
61
|
+
|
62
|
+
# Description of the app and chatbot. This is used for HTML tags.
|
63
|
+
# description = ""
|
64
|
+
|
65
|
+
# Large size content are by default collapsed for a cleaner ui
|
66
|
+
default_collapse_content = true
|
67
|
+
|
68
|
+
# The default value for the expand messages settings.
|
69
|
+
default_expand_messages = false
|
70
|
+
|
71
|
+
# Hide the chain of thought details from the user in the UI.
|
72
|
+
hide_cot = false
|
73
|
+
|
74
|
+
# Link to your github repo. This will add a github button in the UI's header.
|
75
|
+
# github = ""
|
76
|
+
|
77
|
+
# Specify a CSS file that can be used to customize the user interface.
|
78
|
+
# The CSS file can be served from the public directory or via an external link.
|
79
|
+
# custom_css = "/public/test.css"
|
80
|
+
|
81
|
+
# Specify a Javascript file that can be used to customize the user interface.
|
82
|
+
# The Javascript file can be served from the public directory.
|
83
|
+
# custom_js = "/public/test.js"
|
84
|
+
|
85
|
+
# Specify a custom font url.
|
86
|
+
# custom_font = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap"
|
87
|
+
|
88
|
+
# Specify a custom meta image url.
|
89
|
+
# custom_meta_image_url = "https://chainlit-cloud.s3.eu-west-3.amazonaws.com/logo/chainlit_banner.png"
|
90
|
+
|
91
|
+
# Specify a custom build directory for the frontend.
|
92
|
+
# This can be used to customize the frontend code.
|
93
|
+
# Be careful: If this is a relative path, it should not start with a slash.
|
94
|
+
# custom_build = "./public/build"
|
95
|
+
|
96
|
+
[UI.theme]
|
97
|
+
#layout = "wide"
|
98
|
+
#font_family = "Inter, sans-serif"
|
99
|
+
# Override default MUI light theme. (Check theme.ts)
|
100
|
+
[UI.theme.light]
|
101
|
+
#background = "#FAFAFA"
|
102
|
+
#paper = "#FFFFFF"
|
103
|
+
|
104
|
+
[UI.theme.light.primary]
|
105
|
+
#main = "#F80061"
|
106
|
+
#dark = "#980039"
|
107
|
+
#light = "#FFE7EB"
|
108
|
+
|
109
|
+
# Override default MUI dark theme. (Check theme.ts)
|
110
|
+
[UI.theme.dark]
|
111
|
+
#background = "#FAFAFA"
|
112
|
+
#paper = "#FFFFFF"
|
113
|
+
|
114
|
+
[UI.theme.dark.primary]
|
115
|
+
#main = "#F80061"
|
116
|
+
#dark = "#980039"
|
117
|
+
#light = "#FFE7EB"
|
118
|
+
|
119
|
+
|
120
|
+
[meta]
|
121
|
+
generated_by = "1.1.202"
|
@@ -0,0 +1,231 @@
|
|
1
|
+
{
|
2
|
+
"components": {
|
3
|
+
"atoms": {
|
4
|
+
"buttons": {
|
5
|
+
"userButton": {
|
6
|
+
"menu": {
|
7
|
+
"settings": "Settings",
|
8
|
+
"settingsKey": "S",
|
9
|
+
"APIKeys": "API Keys",
|
10
|
+
"logout": "Logout"
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"molecules": {
|
16
|
+
"newChatButton": {
|
17
|
+
"newChat": "New Chat"
|
18
|
+
},
|
19
|
+
"tasklist": {
|
20
|
+
"TaskList": {
|
21
|
+
"title": "\ud83d\uddd2\ufe0f Task List",
|
22
|
+
"loading": "Loading...",
|
23
|
+
"error": "An error occured"
|
24
|
+
}
|
25
|
+
},
|
26
|
+
"attachments": {
|
27
|
+
"cancelUpload": "Cancel upload",
|
28
|
+
"removeAttachment": "Remove attachment"
|
29
|
+
},
|
30
|
+
"newChatDialog": {
|
31
|
+
"createNewChat": "Create new chat?",
|
32
|
+
"clearChat": "This will clear the current messages and start a new chat.",
|
33
|
+
"cancel": "Cancel",
|
34
|
+
"confirm": "Confirm"
|
35
|
+
},
|
36
|
+
"settingsModal": {
|
37
|
+
"settings": "Settings",
|
38
|
+
"expandMessages": "Expand Messages",
|
39
|
+
"hideChainOfThought": "Hide Chain of Thought",
|
40
|
+
"darkMode": "Dark Mode"
|
41
|
+
},
|
42
|
+
"detailsButton": {
|
43
|
+
"using": "Using",
|
44
|
+
"running": "Running",
|
45
|
+
"took_one": "Took {{count}} step",
|
46
|
+
"took_other": "Took {{count}} steps"
|
47
|
+
},
|
48
|
+
"auth": {
|
49
|
+
"authLogin": {
|
50
|
+
"title": "Login to access the app.",
|
51
|
+
"form": {
|
52
|
+
"email": "Email address",
|
53
|
+
"password": "Password",
|
54
|
+
"noAccount": "Don't have an account?",
|
55
|
+
"alreadyHaveAccount": "Already have an account?",
|
56
|
+
"signup": "Sign Up",
|
57
|
+
"signin": "Sign In",
|
58
|
+
"or": "OR",
|
59
|
+
"continue": "Continue",
|
60
|
+
"forgotPassword": "Forgot password?",
|
61
|
+
"passwordMustContain": "Your password must contain:",
|
62
|
+
"emailRequired": "email is a required field",
|
63
|
+
"passwordRequired": "password is a required field"
|
64
|
+
},
|
65
|
+
"error": {
|
66
|
+
"default": "Unable to sign in.",
|
67
|
+
"signin": "Try signing in with a different account.",
|
68
|
+
"oauthsignin": "Try signing in with a different account.",
|
69
|
+
"redirect_uri_mismatch": "The redirect URI is not matching the oauth app configuration.",
|
70
|
+
"oauthcallbackerror": "Try signing in with a different account.",
|
71
|
+
"oauthcreateaccount": "Try signing in with a different account.",
|
72
|
+
"emailcreateaccount": "Try signing in with a different account.",
|
73
|
+
"callback": "Try signing in with a different account.",
|
74
|
+
"oauthaccountnotlinked": "To confirm your identity, sign in with the same account you used originally.",
|
75
|
+
"emailsignin": "The e-mail could not be sent.",
|
76
|
+
"emailverify": "Please verify your email, a new email has been sent.",
|
77
|
+
"credentialssignin": "Sign in failed. Check the details you provided are correct.",
|
78
|
+
"sessionrequired": "Please sign in to access this page."
|
79
|
+
}
|
80
|
+
},
|
81
|
+
"authVerifyEmail": {
|
82
|
+
"almostThere": "You're almost there! We've sent an email to ",
|
83
|
+
"verifyEmailLink": "Please click on the link in that email to complete your signup.",
|
84
|
+
"didNotReceive": "Can't find the email?",
|
85
|
+
"resendEmail": "Resend email",
|
86
|
+
"goBack": "Go Back",
|
87
|
+
"emailSent": "Email sent successfully.",
|
88
|
+
"verifyEmail": "Verify your email address"
|
89
|
+
},
|
90
|
+
"providerButton": {
|
91
|
+
"continue": "Continue with {{provider}}",
|
92
|
+
"signup": "Sign up with {{provider}}"
|
93
|
+
},
|
94
|
+
"authResetPassword": {
|
95
|
+
"newPasswordRequired": "New password is a required field",
|
96
|
+
"passwordsMustMatch": "Passwords must match",
|
97
|
+
"confirmPasswordRequired": "Confirm password is a required field",
|
98
|
+
"newPassword": "New password",
|
99
|
+
"confirmPassword": "Confirm password",
|
100
|
+
"resetPassword": "Reset Password"
|
101
|
+
},
|
102
|
+
"authForgotPassword": {
|
103
|
+
"email": "Email address",
|
104
|
+
"emailRequired": "email is a required field",
|
105
|
+
"emailSent": "Please check the email address {{email}} for instructions to reset your password.",
|
106
|
+
"enterEmail": "Enter your email address and we will send you instructions to reset your password.",
|
107
|
+
"resendEmail": "Resend email",
|
108
|
+
"continue": "Continue",
|
109
|
+
"goBack": "Go Back"
|
110
|
+
}
|
111
|
+
}
|
112
|
+
},
|
113
|
+
"organisms": {
|
114
|
+
"chat": {
|
115
|
+
"history": {
|
116
|
+
"index": {
|
117
|
+
"showHistory": "Show history",
|
118
|
+
"lastInputs": "Last Inputs",
|
119
|
+
"noInputs": "Such empty...",
|
120
|
+
"loading": "Loading..."
|
121
|
+
}
|
122
|
+
},
|
123
|
+
"inputBox": {
|
124
|
+
"input": {
|
125
|
+
"placeholder": "Type your message here..."
|
126
|
+
},
|
127
|
+
"speechButton": {
|
128
|
+
"start": "Start recording",
|
129
|
+
"stop": "Stop recording"
|
130
|
+
},
|
131
|
+
"SubmitButton": {
|
132
|
+
"sendMessage": "Send message",
|
133
|
+
"stopTask": "Stop Task"
|
134
|
+
},
|
135
|
+
"UploadButton": {
|
136
|
+
"attachFiles": "Attach files"
|
137
|
+
},
|
138
|
+
"waterMark": {
|
139
|
+
"text": "Built with"
|
140
|
+
}
|
141
|
+
},
|
142
|
+
"Messages": {
|
143
|
+
"index": {
|
144
|
+
"running": "Running",
|
145
|
+
"executedSuccessfully": "executed successfully",
|
146
|
+
"failed": "failed",
|
147
|
+
"feedbackUpdated": "Feedback updated",
|
148
|
+
"updating": "Updating"
|
149
|
+
}
|
150
|
+
},
|
151
|
+
"dropScreen": {
|
152
|
+
"dropYourFilesHere": "Drop your files here"
|
153
|
+
},
|
154
|
+
"index": {
|
155
|
+
"failedToUpload": "Failed to upload",
|
156
|
+
"cancelledUploadOf": "Cancelled upload of",
|
157
|
+
"couldNotReachServer": "Could not reach the server",
|
158
|
+
"continuingChat": "Continuing previous chat"
|
159
|
+
},
|
160
|
+
"settings": {
|
161
|
+
"settingsPanel": "Settings panel",
|
162
|
+
"reset": "Reset",
|
163
|
+
"cancel": "Cancel",
|
164
|
+
"confirm": "Confirm"
|
165
|
+
}
|
166
|
+
},
|
167
|
+
"threadHistory": {
|
168
|
+
"sidebar": {
|
169
|
+
"filters": {
|
170
|
+
"FeedbackSelect": {
|
171
|
+
"feedbackAll": "Feedback: All",
|
172
|
+
"feedbackPositive": "Feedback: Positive",
|
173
|
+
"feedbackNegative": "Feedback: Negative"
|
174
|
+
},
|
175
|
+
"SearchBar": {
|
176
|
+
"search": "Search"
|
177
|
+
}
|
178
|
+
},
|
179
|
+
"DeleteThreadButton": {
|
180
|
+
"confirmMessage": "This will delete the thread as well as it's messages and elements.",
|
181
|
+
"cancel": "Cancel",
|
182
|
+
"confirm": "Confirm",
|
183
|
+
"deletingChat": "Deleting chat",
|
184
|
+
"chatDeleted": "Chat deleted"
|
185
|
+
},
|
186
|
+
"index": {
|
187
|
+
"pastChats": "Past Chats"
|
188
|
+
},
|
189
|
+
"ThreadList": {
|
190
|
+
"empty": "Empty...",
|
191
|
+
"today": "Today",
|
192
|
+
"yesterday": "Yesterday",
|
193
|
+
"previous7days": "Previous 7 days",
|
194
|
+
"previous30days": "Previous 30 days"
|
195
|
+
},
|
196
|
+
"TriggerButton": {
|
197
|
+
"closeSidebar": "Close sidebar",
|
198
|
+
"openSidebar": "Open sidebar"
|
199
|
+
}
|
200
|
+
},
|
201
|
+
"Thread": {
|
202
|
+
"backToChat": "Go back to chat",
|
203
|
+
"chatCreatedOn": "This chat was created on"
|
204
|
+
}
|
205
|
+
},
|
206
|
+
"header": {
|
207
|
+
"chat": "Chat",
|
208
|
+
"readme": "Readme"
|
209
|
+
}
|
210
|
+
}
|
211
|
+
},
|
212
|
+
"hooks": {
|
213
|
+
"useLLMProviders": {
|
214
|
+
"failedToFetchProviders": "Failed to fetch providers:"
|
215
|
+
}
|
216
|
+
},
|
217
|
+
"pages": {
|
218
|
+
"Design": {},
|
219
|
+
"Env": {
|
220
|
+
"savedSuccessfully": "Saved successfully",
|
221
|
+
"requiredApiKeys": "Required API Keys",
|
222
|
+
"requiredApiKeysInfo": "To use this app, the following API keys are required. The keys are stored on your device's local storage."
|
223
|
+
},
|
224
|
+
"Page": {
|
225
|
+
"notPartOfProject": "You are not part of this project."
|
226
|
+
},
|
227
|
+
"ResumeButton": {
|
228
|
+
"resumeChat": "Resume Chat"
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
langroid/utils/types.py
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
from typing import Any, Optional, Type, TypeVar, Union, get_args, get_origin
|
4
|
+
|
5
|
+
from langroid.pydantic_v1 import BaseModel
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
PrimitiveType = Union[int, float, bool, str]
|
9
|
+
T = TypeVar("T")
|
10
|
+
|
11
|
+
|
12
|
+
def is_instance_of(obj: Any, type_hint: Type[T] | Any) -> bool:
|
13
|
+
"""
|
14
|
+
Check if an object is an instance of a type hint, e.g.
|
15
|
+
to check whether x is of type `List[ToolMessage]` or type `int`
|
16
|
+
"""
|
17
|
+
if type_hint == Any:
|
18
|
+
return True
|
19
|
+
|
20
|
+
if type_hint is type(obj):
|
21
|
+
return True
|
22
|
+
|
23
|
+
origin = get_origin(type_hint)
|
24
|
+
args = get_args(type_hint)
|
25
|
+
|
26
|
+
if origin is Union:
|
27
|
+
return any(is_instance_of(obj, arg) for arg in args)
|
28
|
+
|
29
|
+
if origin: # e.g. List, Dict, Tuple, Set
|
30
|
+
if isinstance(obj, origin):
|
31
|
+
# check if all items in obj are of the required types
|
32
|
+
if args:
|
33
|
+
if isinstance(obj, (list, tuple, set)):
|
34
|
+
return all(is_instance_of(item, args[0]) for item in obj)
|
35
|
+
if isinstance(obj, dict):
|
36
|
+
return all(
|
37
|
+
is_instance_of(k, args[0]) and is_instance_of(v, args[1])
|
38
|
+
for k, v in obj.items()
|
39
|
+
)
|
40
|
+
return True
|
41
|
+
else:
|
42
|
+
return False
|
43
|
+
|
44
|
+
return isinstance(obj, type_hint)
|
45
|
+
|
46
|
+
|
47
|
+
def to_string(msg: Any) -> str:
|
48
|
+
"""
|
49
|
+
Best-effort conversion of arbitrary msg to str.
|
50
|
+
Return empty string if conversion fails.
|
51
|
+
"""
|
52
|
+
if msg is None:
|
53
|
+
return ""
|
54
|
+
if isinstance(msg, str):
|
55
|
+
return msg
|
56
|
+
if isinstance(msg, BaseModel):
|
57
|
+
return msg.json()
|
58
|
+
# last resort: use json.dumps() or str() to make it a str
|
59
|
+
try:
|
60
|
+
return json.dumps(msg)
|
61
|
+
except Exception:
|
62
|
+
try:
|
63
|
+
return str(msg)
|
64
|
+
except Exception as e:
|
65
|
+
logger.error(
|
66
|
+
f"""
|
67
|
+
Error converting msg to str: {e}",
|
68
|
+
""",
|
69
|
+
exc_info=True,
|
70
|
+
)
|
71
|
+
return ""
|
72
|
+
|
73
|
+
|
74
|
+
def from_string(
|
75
|
+
s: str,
|
76
|
+
output_type: Type[PrimitiveType],
|
77
|
+
) -> Optional[PrimitiveType]:
|
78
|
+
if output_type is int:
|
79
|
+
try:
|
80
|
+
return int(s)
|
81
|
+
except ValueError:
|
82
|
+
return None
|
83
|
+
elif output_type is float:
|
84
|
+
try:
|
85
|
+
return float(s)
|
86
|
+
except ValueError:
|
87
|
+
return None
|
88
|
+
elif output_type is bool:
|
89
|
+
return s.lower() in ("true", "yes", "1")
|
90
|
+
elif output_type is str:
|
91
|
+
return s
|
92
|
+
else:
|
93
|
+
return None
|