langroid 0.38.0__py3-none-any.whl → 0.39.1__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 CHANGED
@@ -333,6 +333,11 @@ class Agent(ABC):
333
333
  if hasattr(message_class, "handle_message_fallback") and (
334
334
  inspect.isfunction(message_class.handle_message_fallback)
335
335
  ):
336
+ # When a ToolMessage has a `handle_message_fallback` method,
337
+ # we inject it into the agent as a method, overriding the default
338
+ # `handle_message_fallback` method (which does nothing).
339
+ # It's possible multiple tool messages have a `handle_message_fallback`,
340
+ # in which case, the last one inserted will be used.
336
341
  setattr(
337
342
  self,
338
343
  "handle_message_fallback",
@@ -912,7 +917,7 @@ class Agent(ABC):
912
917
  else:
913
918
  prompt = message
914
919
 
915
- output_len = self.config.llm.max_output_tokens
920
+ output_len = self.config.llm.model_max_output_tokens
916
921
  if self.num_tokens(prompt) + output_len > self.llm.completion_context_length():
917
922
  output_len = self.llm.completion_context_length() - self.num_tokens(prompt)
918
923
  if output_len < self.config.llm.min_output_tokens:
@@ -981,7 +986,7 @@ class Agent(ABC):
981
986
  # show rich spinner only if not streaming!
982
987
  cm = status("LLM responding to message...")
983
988
  stack.enter_context(cm)
984
- output_len = self.config.llm.max_output_tokens
989
+ output_len = self.config.llm.model_max_output_tokens
985
990
  if (
986
991
  self.num_tokens(prompt) + output_len
987
992
  > self.llm.completion_context_length()
@@ -1866,7 +1871,7 @@ class Agent(ABC):
1866
1871
  cumul_cost = format(tot_cost, ".4f")
1867
1872
  assert isinstance(self.llm, LanguageModel)
1868
1873
  context_length = self.llm.chat_context_length()
1869
- max_out = self.config.llm.max_output_tokens
1874
+ max_out = self.config.llm.model_max_output_tokens
1870
1875
 
1871
1876
  llm_model = (
1872
1877
  "no-LLM" if self.config.llm is None else self.llm.config.chat_model
@@ -5,7 +5,7 @@ import logging
5
5
  import textwrap
6
6
  from contextlib import ExitStack
7
7
  from inspect import isclass
8
- from typing import Dict, List, Optional, Self, Set, Tuple, Type, Union, cast
8
+ from typing import Any, Dict, List, Optional, Self, Set, Tuple, Type, Union, cast
9
9
 
10
10
  import openai
11
11
  from rich import print
@@ -31,6 +31,7 @@ from langroid.language_models.base import (
31
31
  ToolChoiceTypes,
32
32
  )
33
33
  from langroid.language_models.openai_gpt import OpenAIGPT
34
+ from langroid.mytypes import Entity, NonToolAction
34
35
  from langroid.pydantic_v1 import BaseModel, ValidationError
35
36
  from langroid.utils.configuration import settings
36
37
  from langroid.utils.object_registry import ObjectRegistry
@@ -52,6 +53,7 @@ class ChatAgentConfig(AgentConfig):
52
53
  user_message: user message to include in message sequence.
53
54
  Used only if `task` is not specified in the constructor.
54
55
  use_tools: whether to use our own ToolMessages mechanism
56
+ handle_llm_no_tool (NonToolAction|str): routing when LLM generates non-tool msg.
55
57
  use_functions_api: whether to use functions/tools native to the LLM API
56
58
  (e.g. OpenAI's `function_call` or `tool_call` mechanism)
57
59
  use_tools_api: When `use_functions_api` is True, if this is also True,
@@ -84,6 +86,7 @@ class ChatAgentConfig(AgentConfig):
84
86
 
85
87
  system_message: str = "You are a helpful assistant."
86
88
  user_message: Optional[str] = None
89
+ handle_llm_no_tool: NonToolAction | None = None
87
90
  use_tools: bool = False
88
91
  use_functions_api: bool = True
89
92
  use_tools_api: bool = False
@@ -579,6 +582,31 @@ class ChatAgent(Agent):
579
582
  # remove leading and trailing newlines and other whitespace
580
583
  return LLMMessage(role=Role.SYSTEM, content=content.strip())
581
584
 
585
+ def handle_message_fallback(self, msg: str | ChatDocument) -> Any:
586
+ """
587
+ Fallback method for the "no-tools" scenario.
588
+ Users the self.config.non_tool_routing to determine the action to take.
589
+
590
+ This method can be overridden by subclasses, e.g.,
591
+ to create a "reminder" message when a tool is expected but the LLM "forgot"
592
+ to generate one.
593
+
594
+ Args:
595
+ msg (str | ChatDocument): The input msg to handle
596
+ Returns:
597
+ Any: The result of the handler method
598
+ """
599
+ if self.config.handle_llm_no_tool is None:
600
+ return None
601
+ if isinstance(msg, ChatDocument) and msg.metadata.sender == Entity.LLM:
602
+ from langroid.agent.tools.orchestration import AgentDoneTool, ForwardTool
603
+
604
+ match self.config.handle_llm_no_tool:
605
+ case NonToolAction.FORWARD_USER:
606
+ return ForwardTool(agent="User")
607
+ case NonToolAction.DONE:
608
+ return AgentDoneTool(content=msg.content, tools=msg.tool_messages)
609
+
582
610
  def unhandled_tools(self) -> set[str]:
583
611
  """The set of tools that are known but not handled.
584
612
  Useful in task flow: an agent can refuse to accept an incoming msg
@@ -1460,11 +1488,11 @@ class ChatAgent(Agent):
1460
1488
  self.message_history.extend(llm_msgs)
1461
1489
 
1462
1490
  hist = self.message_history
1463
- output_len = self.config.llm.max_output_tokens
1491
+ output_len = self.config.llm.model_max_output_tokens
1464
1492
  if (
1465
1493
  truncate
1466
1494
  and self.chat_num_tokens(hist)
1467
- > self.llm.chat_context_length() - self.config.llm.max_output_tokens
1495
+ > self.llm.chat_context_length() - self.config.llm.model_max_output_tokens
1468
1496
  ):
1469
1497
  # chat + output > max context length,
1470
1498
  # so first try to shorten requested output len to fit.
@@ -1489,7 +1517,7 @@ class ChatAgent(Agent):
1489
1517
  The message history is longer than the max chat context
1490
1518
  length allowed, and we have run out of messages to drop.
1491
1519
  HINT: In your `OpenAIGPTConfig` object, try increasing
1492
- `chat_context_length` or decreasing `max_output_tokens`.
1520
+ `chat_context_length` or decreasing `model_max_output_tokens`.
1493
1521
  """
1494
1522
  )
1495
1523
  # drop the second message, i.e. first msg after the sys msg
@@ -1638,12 +1666,12 @@ class ChatAgent(Agent):
1638
1666
  Args:
1639
1667
  messages: seq of messages (with role, content fields) sent to LLM
1640
1668
  output_len: max number of tokens expected in response.
1641
- If None, use the LLM's default max_output_tokens.
1669
+ If None, use the LLM's default model_max_output_tokens.
1642
1670
  Returns:
1643
1671
  Document (i.e. with fields "content", "metadata")
1644
1672
  """
1645
1673
  assert self.config.llm is not None and self.llm is not None
1646
- output_len = output_len or self.config.llm.max_output_tokens
1674
+ output_len = output_len or self.config.llm.model_max_output_tokens
1647
1675
  streamer = noop_fn
1648
1676
  if self.llm.get_stream():
1649
1677
  streamer = self.callbacks.start_llm_stream()
@@ -1713,7 +1741,7 @@ class ChatAgent(Agent):
1713
1741
  Async version of `llm_response_messages`. See there for details.
1714
1742
  """
1715
1743
  assert self.config.llm is not None and self.llm is not None
1716
- output_len = output_len or self.config.llm.max_output_tokens
1744
+ output_len = output_len or self.config.llm.model_max_output_tokens
1717
1745
  functions, fun_call, tools, force_tool, output_format = self._function_args()
1718
1746
  assert self.llm is not None
1719
1747
 
@@ -1565,7 +1565,7 @@ class DocChatAgent(ChatAgent):
1565
1565
  tot_tokens = self.parser.num_tokens(full_text)
1566
1566
  MAX_INPUT_TOKENS = (
1567
1567
  self.llm.completion_context_length()
1568
- - self.config.llm.max_output_tokens
1568
+ - self.config.llm.model_max_output_tokens
1569
1569
  - 100
1570
1570
  )
1571
1571
  if tot_tokens > MAX_INPUT_TOKENS:
@@ -15,14 +15,13 @@ from .base import (
15
15
  LLMTokenUsage,
16
16
  LLMResponse,
17
17
  )
18
- from .openai_gpt import (
18
+ from .model_info import (
19
19
  OpenAIChatModel,
20
20
  AnthropicModel,
21
21
  GeminiModel,
22
22
  OpenAICompletionModel,
23
- OpenAIGPTConfig,
24
- OpenAIGPT,
25
23
  )
24
+ from .openai_gpt import OpenAIGPTConfig, OpenAIGPT, OpenAICallParams
26
25
  from .mock_lm import MockLM, MockLMConfig
27
26
  from .azure_openai import AzureConfig, AzureGPT
28
27
 
@@ -32,6 +31,7 @@ __all__ = [
32
31
  "config",
33
32
  "base",
34
33
  "openai_gpt",
34
+ "model_info",
35
35
  "azure_openai",
36
36
  "prompt_formatter",
37
37
  "StreamEventType",
@@ -48,6 +48,7 @@ __all__ = [
48
48
  "OpenAICompletionModel",
49
49
  "OpenAIGPTConfig",
50
50
  "OpenAIGPT",
51
+ "OpenAICallParams",
51
52
  "AzureConfig",
52
53
  "AzureGPT",
53
54
  "MockLM",
@@ -19,6 +19,7 @@ from typing import (
19
19
 
20
20
  from langroid.cachedb.base import CacheDBConfig
21
21
  from langroid.cachedb.redis_cachedb import RedisCacheConfig
22
+ from langroid.language_models.model_info import get_model_info
22
23
  from langroid.parsing.agent_chats import parse_message
23
24
  from langroid.parsing.parse_json import parse_imperfect_json, top_level_json_field
24
25
  from langroid.prompts.dialog import collate_chat_history
@@ -60,6 +61,7 @@ class LLMConfig(BaseSettings):
60
61
  streamer_async: Optional[Callable[..., Awaitable[None]]] = async_noop_fn
61
62
  api_base: str | None = None
62
63
  formatter: None | str = None
64
+ max_output_tokens: int | None = 8192 # specify None to use model_max_output_tokens
63
65
  timeout: int = 20 # timeout for API requests
64
66
  chat_model: str = ""
65
67
  completion_model: str = ""
@@ -67,7 +69,6 @@ class LLMConfig(BaseSettings):
67
69
  chat_context_length: int = 8000
68
70
  async_stream_quiet: bool = True # suppress streaming output in async mode?
69
71
  completion_context_length: int = 8000
70
- max_output_tokens: int = 1024 # generate at most this many tokens
71
72
  # if input length + max_output_tokens > context length of model,
72
73
  # we will try shortening requested output
73
74
  min_output_tokens: int = 64
@@ -84,6 +85,12 @@ class LLMConfig(BaseSettings):
84
85
  chat_cost_per_1k_tokens: Tuple[float, float] = (0.0, 0.0)
85
86
  completion_cost_per_1k_tokens: Tuple[float, float] = (0.0, 0.0)
86
87
 
88
+ @property
89
+ def model_max_output_tokens(self) -> int:
90
+ return (
91
+ self.max_output_tokens or get_model_info(self.chat_model).max_output_tokens
92
+ )
93
+
87
94
 
88
95
  class LLMFunctionCall(BaseModel):
89
96
  """
@@ -0,0 +1,307 @@
1
+ from enum import Enum
2
+ from typing import Dict, List, Optional
3
+
4
+ from langroid.pydantic_v1 import BaseModel
5
+
6
+
7
+ class ModelProvider(str, Enum):
8
+ """Enum for model providers"""
9
+
10
+ OPENAI = "openai"
11
+ ANTHROPIC = "anthropic"
12
+ DEEPSEEK = "deepseek"
13
+ GOOGLE = "google"
14
+ UNKNOWN = "unknown"
15
+
16
+
17
+ class ModelName(str, Enum):
18
+ """Parent class for all model name enums"""
19
+
20
+ pass
21
+
22
+
23
+ class OpenAIChatModel(ModelName):
24
+ """Enum for OpenAI Chat models"""
25
+
26
+ GPT3_5_TURBO = "gpt-3.5-turbo-1106"
27
+ GPT4 = "gpt-4"
28
+ GPT4_TURBO = "gpt-4-turbo"
29
+ GPT4o = "gpt-4o"
30
+ GPT4o_MINI = "gpt-4o-mini"
31
+ O1 = "o1"
32
+ O1_MINI = "o1-mini"
33
+ O3_MINI = "o3-mini"
34
+
35
+
36
+ class OpenAICompletionModel(str, Enum):
37
+ """Enum for OpenAI Completion models"""
38
+
39
+ DAVINCI = "davinci-002"
40
+ BABBAGE = "babbage-002"
41
+
42
+
43
+ class AnthropicModel(ModelName):
44
+ """Enum for Anthropic models"""
45
+
46
+ CLAUDE_3_5_SONNET = "claude-3-5-sonnet-latest"
47
+ CLAUDE_3_OPUS = "claude-3-opus-latest"
48
+ CLAUDE_3_SONNET = "claude-3-sonnet-20240229"
49
+ CLAUDE_3_HAIKU = "claude-3-haiku-20240307"
50
+
51
+
52
+ class DeepSeekModel(ModelName):
53
+ """Enum for DeepSeek models direct from DeepSeek API"""
54
+
55
+ DEEPSEEK = "deepseek/deepseek-chat"
56
+ DEEPSEEK_R1 = "deepseek/deepseek-reasoner"
57
+
58
+
59
+ class GeminiModel(ModelName):
60
+ """Enum for Gemini models"""
61
+
62
+ GEMINI_1_5_FLASH = "gemini/gemini-1.5-flash"
63
+ GEMINI_1_5_FLASH_8B = "gemini/gemini-1.5-flash-8b"
64
+ GEMINI_1_5_PRO = "gemini/gemini-1.5-pro"
65
+ GEMINI_2_FLASH = "gemini/gemini-2.0-flash-exp"
66
+ GEMINI_2_FLASH_THINKING = "gemini/gemini-2.0-flash-thinking-exp"
67
+
68
+
69
+ class ModelInfo(BaseModel):
70
+ """
71
+ Consolidated information about LLM, related to capacity, cost and API
72
+ idiosyncrasies. Reasonable defaults for all params in case there's no
73
+ specific info available.
74
+ """
75
+
76
+ name: str = "unknown"
77
+ provider: ModelProvider = ModelProvider.UNKNOWN
78
+ context_length: int = 16_000
79
+ max_cot_tokens: int = 0 # max chain of thought (thinking) tokens where applicable
80
+ max_output_tokens: int = 8192 # Maximum number of output tokens - model dependent
81
+ input_cost_per_million: float = 0.0 # Cost in USD per million input tokens
82
+ output_cost_per_million: float = 0.0 # Cost in USD per million output tokens
83
+ allows_streaming: bool = True # Whether model supports streaming output
84
+ allows_system_message: bool = True # Whether model supports system messages
85
+ rename_params: Dict[str, str] = {} # Rename parameters for OpenAI API
86
+ unsupported_params: List[str] = []
87
+ has_structured_output: bool = False # Does model API support structured output?
88
+ has_tools: bool = True # Does model API support tools/function-calling?
89
+ needs_first_user_message: bool = False # Does API need first msg to be from user?
90
+ description: Optional[str] = None
91
+
92
+
93
+ # Model information registry
94
+ MODEL_INFO: Dict[str, ModelInfo] = {
95
+ # OpenAI Models
96
+ OpenAICompletionModel.DAVINCI.value: ModelInfo(
97
+ name=OpenAICompletionModel.DAVINCI.value,
98
+ provider=ModelProvider.OPENAI,
99
+ context_length=4096,
100
+ max_output_tokens=4096,
101
+ input_cost_per_million=2.0,
102
+ output_cost_per_million=2.0,
103
+ description="Davinci-002",
104
+ ),
105
+ OpenAICompletionModel.BABBAGE.value: ModelInfo(
106
+ name=OpenAICompletionModel.BABBAGE.value,
107
+ provider=ModelProvider.OPENAI,
108
+ context_length=4096,
109
+ max_output_tokens=4096,
110
+ input_cost_per_million=0.40,
111
+ output_cost_per_million=0.40,
112
+ description="Babbage-002",
113
+ ),
114
+ OpenAIChatModel.GPT3_5_TURBO.value: ModelInfo(
115
+ name=OpenAIChatModel.GPT3_5_TURBO.value,
116
+ provider=ModelProvider.OPENAI,
117
+ context_length=16_385,
118
+ max_output_tokens=4096,
119
+ input_cost_per_million=0.50,
120
+ output_cost_per_million=1.50,
121
+ description="GPT-3.5 Turbo",
122
+ ),
123
+ OpenAIChatModel.GPT4.value: ModelInfo(
124
+ name=OpenAIChatModel.GPT4.value,
125
+ provider=ModelProvider.OPENAI,
126
+ context_length=8192,
127
+ max_output_tokens=8192,
128
+ input_cost_per_million=30.0,
129
+ output_cost_per_million=60.0,
130
+ description="GPT-4 (8K context)",
131
+ ),
132
+ OpenAIChatModel.GPT4_TURBO.value: ModelInfo(
133
+ name=OpenAIChatModel.GPT4_TURBO.value,
134
+ provider=ModelProvider.OPENAI,
135
+ context_length=128_000,
136
+ max_output_tokens=4096,
137
+ input_cost_per_million=10.0,
138
+ output_cost_per_million=30.0,
139
+ description="GPT-4 Turbo",
140
+ ),
141
+ OpenAIChatModel.GPT4o.value: ModelInfo(
142
+ name=OpenAIChatModel.GPT4o.value,
143
+ provider=ModelProvider.OPENAI,
144
+ context_length=128_000,
145
+ max_output_tokens=16_384,
146
+ input_cost_per_million=2.5,
147
+ output_cost_per_million=10.0,
148
+ has_structured_output=True,
149
+ description="GPT-4o (128K context)",
150
+ ),
151
+ OpenAIChatModel.GPT4o_MINI.value: ModelInfo(
152
+ name=OpenAIChatModel.GPT4o_MINI.value,
153
+ provider=ModelProvider.OPENAI,
154
+ context_length=128_000,
155
+ max_output_tokens=16_384,
156
+ input_cost_per_million=0.15,
157
+ output_cost_per_million=0.60,
158
+ has_structured_output=True,
159
+ description="GPT-4o Mini",
160
+ ),
161
+ OpenAIChatModel.O1.value: ModelInfo(
162
+ name=OpenAIChatModel.O1.value,
163
+ provider=ModelProvider.OPENAI,
164
+ context_length=200_000,
165
+ max_output_tokens=100_000,
166
+ input_cost_per_million=15.0,
167
+ output_cost_per_million=60.0,
168
+ allows_streaming=False,
169
+ allows_system_message=False,
170
+ unsupported_params=["temperature", "stream"],
171
+ rename_params={"max_tokens": "max_completion_tokens"},
172
+ has_tools=False,
173
+ description="O1 Reasoning LM",
174
+ ),
175
+ OpenAIChatModel.O1_MINI.value: ModelInfo(
176
+ name=OpenAIChatModel.O1_MINI.value,
177
+ provider=ModelProvider.OPENAI,
178
+ context_length=128_000,
179
+ max_output_tokens=65_536,
180
+ input_cost_per_million=1.1,
181
+ output_cost_per_million=4.4,
182
+ allows_streaming=False,
183
+ allows_system_message=False,
184
+ unsupported_params=["temperature", "stream"],
185
+ rename_params={"max_tokens": "max_completion_tokens"},
186
+ has_tools=False,
187
+ description="O1 Mini Reasoning LM",
188
+ ),
189
+ OpenAIChatModel.O3_MINI.value: ModelInfo(
190
+ name=OpenAIChatModel.O3_MINI.value,
191
+ provider=ModelProvider.OPENAI,
192
+ context_length=200_000,
193
+ max_output_tokens=100_000,
194
+ input_cost_per_million=1.1,
195
+ output_cost_per_million=4.4,
196
+ allows_streaming=False,
197
+ allows_system_message=False,
198
+ unsupported_params=["temperature", "stream"],
199
+ rename_params={"max_tokens": "max_completion_tokens"},
200
+ has_tools=False,
201
+ description="O3 Mini Reasoning LM",
202
+ ),
203
+ # Anthropic Models
204
+ AnthropicModel.CLAUDE_3_5_SONNET.value: ModelInfo(
205
+ name=AnthropicModel.CLAUDE_3_5_SONNET.value,
206
+ provider=ModelProvider.ANTHROPIC,
207
+ context_length=200_000,
208
+ max_output_tokens=8192,
209
+ input_cost_per_million=3.0,
210
+ output_cost_per_million=15.0,
211
+ description="Claude 3.5 Sonnet",
212
+ ),
213
+ AnthropicModel.CLAUDE_3_OPUS.value: ModelInfo(
214
+ name=AnthropicModel.CLAUDE_3_OPUS.value,
215
+ provider=ModelProvider.ANTHROPIC,
216
+ context_length=200_000,
217
+ max_output_tokens=4096,
218
+ input_cost_per_million=15.0,
219
+ output_cost_per_million=75.0,
220
+ description="Claude 3 Opus",
221
+ ),
222
+ AnthropicModel.CLAUDE_3_SONNET.value: ModelInfo(
223
+ name=AnthropicModel.CLAUDE_3_SONNET.value,
224
+ provider=ModelProvider.ANTHROPIC,
225
+ context_length=200_000,
226
+ max_output_tokens=4096,
227
+ input_cost_per_million=3.0,
228
+ output_cost_per_million=15.0,
229
+ description="Claude 3 Sonnet",
230
+ ),
231
+ AnthropicModel.CLAUDE_3_HAIKU.value: ModelInfo(
232
+ name=AnthropicModel.CLAUDE_3_HAIKU.value,
233
+ provider=ModelProvider.ANTHROPIC,
234
+ context_length=200_000,
235
+ max_output_tokens=4096,
236
+ input_cost_per_million=0.25,
237
+ output_cost_per_million=1.25,
238
+ description="Claude 3 Haiku",
239
+ ),
240
+ # DeepSeek Models
241
+ DeepSeekModel.DEEPSEEK.value: ModelInfo(
242
+ name=DeepSeekModel.DEEPSEEK.value,
243
+ provider=ModelProvider.DEEPSEEK,
244
+ context_length=64_000,
245
+ max_output_tokens=8_000,
246
+ input_cost_per_million=0.27,
247
+ output_cost_per_million=1.10,
248
+ description="DeepSeek Chat",
249
+ ),
250
+ DeepSeekModel.DEEPSEEK_R1.value: ModelInfo(
251
+ name=DeepSeekModel.DEEPSEEK_R1.value,
252
+ provider=ModelProvider.DEEPSEEK,
253
+ context_length=64_000,
254
+ max_output_tokens=8_000,
255
+ input_cost_per_million=0.55,
256
+ output_cost_per_million=2.19,
257
+ description="DeepSeek-R1 Reasoning LM",
258
+ ),
259
+ # Gemini Models
260
+ GeminiModel.GEMINI_2_FLASH.value: ModelInfo(
261
+ name=GeminiModel.GEMINI_2_FLASH.value,
262
+ provider=ModelProvider.GOOGLE,
263
+ context_length=1_056_768,
264
+ max_output_tokens=8192,
265
+ rename_params={"max_tokens": "max_completion_tokens"},
266
+ description="Gemini 2.0 Flash",
267
+ ),
268
+ GeminiModel.GEMINI_1_5_FLASH.value: ModelInfo(
269
+ name=GeminiModel.GEMINI_1_5_FLASH.value,
270
+ provider=ModelProvider.GOOGLE,
271
+ context_length=1_056_768,
272
+ max_output_tokens=8192,
273
+ rename_params={"max_tokens": "max_completion_tokens"},
274
+ description="Gemini 1.5 Flash",
275
+ ),
276
+ GeminiModel.GEMINI_1_5_FLASH_8B.value: ModelInfo(
277
+ name=GeminiModel.GEMINI_1_5_FLASH_8B.value,
278
+ provider=ModelProvider.GOOGLE,
279
+ context_length=1_000_000,
280
+ max_output_tokens=8192,
281
+ rename_params={"max_tokens": "max_completion_tokens"},
282
+ description="Gemini 1.5 Flash 8B",
283
+ ),
284
+ GeminiModel.GEMINI_1_5_PRO.value: ModelInfo(
285
+ name=GeminiModel.GEMINI_1_5_PRO.value,
286
+ provider=ModelProvider.GOOGLE,
287
+ context_length=2_000_000,
288
+ max_output_tokens=8192,
289
+ rename_params={"max_tokens": "max_completion_tokens"},
290
+ description="Gemini 1.5 Pro",
291
+ ),
292
+ GeminiModel.GEMINI_2_FLASH_THINKING.value: ModelInfo(
293
+ name=GeminiModel.GEMINI_2_FLASH_THINKING.value,
294
+ provider=ModelProvider.GOOGLE,
295
+ context_length=1_000_000,
296
+ max_output_tokens=64_000,
297
+ rename_params={"max_tokens": "max_completion_tokens"},
298
+ description="Gemini 2.0 Flash Thinking",
299
+ ),
300
+ }
301
+
302
+
303
+ def get_model_info(model: str | ModelName) -> ModelInfo:
304
+ """Get model information by name or enum value"""
305
+ if isinstance(model, str):
306
+ return MODEL_INFO.get(model) or ModelInfo()
307
+ return MODEL_INFO.get(model.value) or ModelInfo()
@@ -5,7 +5,6 @@ import os
5
5
  import sys
6
6
  import warnings
7
7
  from collections import defaultdict
8
- from enum import Enum
9
8
  from functools import cache
10
9
  from itertools import chain
11
10
  from typing import (
@@ -47,6 +46,17 @@ from langroid.language_models.base import (
47
46
  ToolChoiceTypes,
48
47
  )
49
48
  from langroid.language_models.config import HFPromptFormatterConfig
49
+ from langroid.language_models.model_info import (
50
+ DeepSeekModel,
51
+ GeminiModel,
52
+ get_model_info,
53
+ )
54
+ from langroid.language_models.model_info import (
55
+ OpenAIChatModel as OpenAIChatModel,
56
+ )
57
+ from langroid.language_models.model_info import (
58
+ OpenAICompletionModel as OpenAICompletionModel,
59
+ )
50
60
  from langroid.language_models.prompt_formatter.hf_formatter import (
51
61
  HFFormatter,
52
62
  find_hf_formatter,
@@ -79,118 +89,19 @@ VLLM_API_KEY = os.environ.get("VLLM_API_KEY", DUMMY_API_KEY)
79
89
  LLAMACPP_API_KEY = os.environ.get("LLAMA_API_KEY", DUMMY_API_KEY)
80
90
 
81
91
 
82
- class DeepSeekModel(str, Enum):
83
- DEEPSEEK = "deepseek/deepseek-chat"
84
-
85
-
86
- class AnthropicModel(str, Enum):
87
- """Enum for Anthropic models"""
88
-
89
- CLAUDE_3_5_SONNET = "claude-3-5-sonnet-latest"
90
- CLAUDE_3_OPUS = "claude-3-opus-20240229"
91
- CLAUDE_3_SONNET = "claude-3-sonnet-20240229"
92
- CLAUDE_3_HAIKU = "claude-3-turbo-20240307"
93
-
94
-
95
- class OpenAIChatModel(str, Enum):
96
- """Enum for OpenAI Chat models"""
97
-
98
- GPT3_5_TURBO = "gpt-3.5-turbo-1106"
99
- GPT4 = "gpt-4"
100
- GPT4_32K = "gpt-4-32k"
101
- GPT4_TURBO = "gpt-4-turbo"
102
- GPT4o = "gpt-4o"
103
- GPT4o_MINI = "gpt-4o-mini"
104
- O1_PREVIEW = "o1-preview"
105
- O1_MINI = "o1-mini"
106
-
107
-
108
- class GeminiModel(str, Enum):
109
- """Enum for Gemini models"""
110
-
111
- GEMINI_1_5_FLASH = "gemini/gemini-1.5-flash"
112
- GEMINI_1_5_FLASH_8B = "gemini/gemini-1.5-flash-8b"
113
- GEMINI_1_5_PRO = "gemini/gemini-1.5-pro"
114
- GEMINI_2_FLASH = "gemini/gemini-2.0-flash-exp"
115
-
116
-
117
- class OpenAICompletionModel(str, Enum):
118
- """Enum for OpenAI Completion models"""
119
-
120
- TEXT_DA_VINCI_003 = "text-davinci-003" # deprecated
121
- GPT3_5_TURBO_INSTRUCT = "gpt-3.5-turbo-instruct"
122
-
123
-
124
- _context_length: Dict[str, int] = {
125
- # can add other non-openAI models here
126
- OpenAIChatModel.GPT3_5_TURBO: 16_385,
127
- OpenAIChatModel.GPT4: 8192,
128
- OpenAIChatModel.GPT4_32K: 32_768,
129
- OpenAIChatModel.GPT4_TURBO: 128_000,
130
- OpenAIChatModel.GPT4o: 128_000,
131
- OpenAIChatModel.GPT4o_MINI: 128_000,
132
- OpenAIChatModel.O1_PREVIEW: 128_000,
133
- OpenAIChatModel.O1_MINI: 128_000,
134
- OpenAICompletionModel.TEXT_DA_VINCI_003: 4096,
135
- AnthropicModel.CLAUDE_3_5_SONNET: 200_000,
136
- AnthropicModel.CLAUDE_3_OPUS: 200_000,
137
- AnthropicModel.CLAUDE_3_SONNET: 200_000,
138
- AnthropicModel.CLAUDE_3_HAIKU: 200_000,
139
- DeepSeekModel.DEEPSEEK: 64_000,
140
- GeminiModel.GEMINI_2_FLASH: 1_000_000,
141
- GeminiModel.GEMINI_1_5_FLASH: 1_000_000,
142
- GeminiModel.GEMINI_1_5_FLASH_8B: 1_000_000,
143
- GeminiModel.GEMINI_1_5_PRO: 2_000_000,
144
- }
145
-
146
- _cost_per_1k_tokens: Dict[str, Tuple[float, float]] = {
147
- # can add other non-openAI models here.
148
- # model => (prompt cost, generation cost) in USD
149
- OpenAIChatModel.GPT3_5_TURBO: (0.001, 0.002),
150
- OpenAIChatModel.GPT4: (0.03, 0.06), # 8K context
151
- OpenAIChatModel.GPT4_TURBO: (0.01, 0.03), # 128K context
152
- OpenAIChatModel.GPT4o: (0.0025, 0.010), # 128K context
153
- OpenAIChatModel.GPT4o_MINI: (0.00015, 0.0006), # 128K context
154
- OpenAIChatModel.O1_PREVIEW: (0.015, 0.060), # 128K context
155
- OpenAIChatModel.O1_MINI: (0.003, 0.012), # 128K context
156
- AnthropicModel.CLAUDE_3_5_SONNET: (0.003, 0.015),
157
- AnthropicModel.CLAUDE_3_OPUS: (0.015, 0.075),
158
- AnthropicModel.CLAUDE_3_SONNET: (0.003, 0.015),
159
- AnthropicModel.CLAUDE_3_HAIKU: (0.00025, 0.00125),
160
- DeepSeekModel.DEEPSEEK: (0.00014, 0.00028),
161
- # Gemini models have complex pricing based on input-len
162
- }
163
-
164
-
165
- openAIChatModelPreferenceList = [
92
+ openai_chat_model_pref_list = [
166
93
  OpenAIChatModel.GPT4o,
167
- OpenAIChatModel.GPT4_TURBO,
168
- OpenAIChatModel.GPT4,
169
94
  OpenAIChatModel.GPT4o_MINI,
170
95
  OpenAIChatModel.O1_MINI,
171
- OpenAIChatModel.O1_PREVIEW,
96
+ OpenAIChatModel.O1,
172
97
  OpenAIChatModel.GPT3_5_TURBO,
173
98
  ]
174
99
 
175
- openAICompletionModelPreferenceList = [
176
- OpenAICompletionModel.GPT3_5_TURBO_INSTRUCT,
177
- OpenAICompletionModel.TEXT_DA_VINCI_003,
178
- ]
179
-
180
- openAIStructuredOutputList = [
181
- OpenAIChatModel.GPT4o_MINI,
182
- OpenAIChatModel.GPT4o,
183
- ]
184
-
185
- NON_STREAMING_MODELS = [
186
- OpenAIChatModel.O1_MINI,
187
- OpenAIChatModel.O1_PREVIEW,
100
+ openai_completion_model_pref_list = [
101
+ OpenAICompletionModel.DAVINCI,
102
+ OpenAICompletionModel.BABBAGE,
188
103
  ]
189
104
 
190
- NON_SYSTEM_MESSAGE_MODELS = [
191
- OpenAIChatModel.O1_MINI,
192
- OpenAIChatModel.O1_PREVIEW,
193
- ]
194
105
 
195
106
  if "OPENAI_API_KEY" in os.environ:
196
107
  try:
@@ -218,22 +129,22 @@ if "OPENAI_API_KEY" in os.environ:
218
129
  else:
219
130
  available_models = set()
220
131
 
221
- defaultOpenAIChatModel = next(
132
+ default_openai_chat_model = next(
222
133
  chain(
223
134
  filter(
224
135
  lambda m: m.value in available_models,
225
- openAIChatModelPreferenceList,
136
+ openai_chat_model_pref_list,
226
137
  ),
227
- [OpenAIChatModel.GPT4_TURBO],
138
+ [OpenAIChatModel.GPT4o],
228
139
  )
229
140
  )
230
- defaultOpenAICompletionModel = next(
141
+ default_openai_completion_model = next(
231
142
  chain(
232
143
  filter(
233
144
  lambda m: m.value in available_models,
234
- openAICompletionModelPreferenceList,
145
+ openai_completion_model_pref_list,
235
146
  ),
236
- [OpenAICompletionModel.GPT3_5_TURBO_INSTRUCT],
147
+ [OpenAICompletionModel.DAVINCI],
237
148
  )
238
149
  )
239
150
 
@@ -245,8 +156,9 @@ class AccessWarning(Warning):
245
156
  @cache
246
157
  def gpt_3_5_warning() -> None:
247
158
  warnings.warn(
248
- """
249
- GPT-4 is not available, falling back to GPT-3.5.
159
+ f"""
160
+ {OpenAIChatModel.GPT4o} is not available,
161
+ falling back to {OpenAIChatModel.GPT3_5_TURBO}.
250
162
  Examples may not work properly and unexpected behavior may occur.
251
163
  Adjustments to prompts may be necessary.
252
164
  """,
@@ -285,6 +197,7 @@ class OpenAICallParams(BaseModel):
285
197
  logit_bias: Dict[int, float] | None = None # token_id -> bias
286
198
  logprobs: bool = False
287
199
  top_p: float | None = 1.0
200
+ reasoning_effort: str | None = None # or "low" or "high" or "medium"
288
201
  top_logprobs: int | None = None # if int, requires logprobs=True
289
202
  n: int = 1 # how many completions to generate (n > 1 is NOT handled now)
290
203
  stop: str | List[str] | None = None # (list of) stop sequence(s)
@@ -310,7 +223,6 @@ class OpenAIGPTConfig(LLMConfig):
310
223
  api_base: str | None = None # used for local or other non-OpenAI models
311
224
  litellm: bool = False # use litellm api?
312
225
  ollama: bool = False # use ollama's OpenAI-compatible endpoint?
313
- max_output_tokens: int = 1024
314
226
  min_output_tokens: int = 1
315
227
  use_chat_for_completion = True # do not change this, for OpenAI models!
316
228
  timeout: int = 20
@@ -318,8 +230,8 @@ class OpenAIGPTConfig(LLMConfig):
318
230
  seed: int | None = 42
319
231
  params: OpenAICallParams | None = None
320
232
  # these can be any model name that is served at an OpenAI-compatible API end point
321
- chat_model: str = defaultOpenAIChatModel
322
- completion_model: str = defaultOpenAICompletionModel
233
+ chat_model: str = default_openai_chat_model
234
+ completion_model: str = default_openai_completion_model
323
235
  run_on_first_use: Callable[[], None] = noop
324
236
  parallel_tool_calls: Optional[bool] = None
325
237
  # Supports constrained decoding which enforces that the output of the LLM
@@ -345,7 +257,7 @@ class OpenAIGPTConfig(LLMConfig):
345
257
  warn_gpt_3_5 = (
346
258
  "chat_model" not in kwargs.keys()
347
259
  and not local_model
348
- and defaultOpenAIChatModel == OpenAIChatModel.GPT3_5_TURBO
260
+ and default_openai_chat_model == OpenAIChatModel.GPT3_5_TURBO
349
261
  )
350
262
 
351
263
  if warn_gpt_3_5:
@@ -554,7 +466,7 @@ class OpenAIGPT(LanguageModel):
554
466
  self.supports_strict_tools = self.api_base is None
555
467
  self.supports_json_schema = (
556
468
  self.api_base is None
557
- and self.config.chat_model in openAIStructuredOutputList
469
+ and get_model_info(self.config.chat_model).has_structured_output
558
470
  )
559
471
 
560
472
  if settings.chat_model != "":
@@ -704,10 +616,10 @@ class OpenAIGPT(LanguageModel):
704
616
  return self.config.chat_model in openai_chat_models
705
617
 
706
618
  def supports_functions_or_tools(self) -> bool:
707
- return self.is_openai_chat_model() and self.config.chat_model not in [
708
- OpenAIChatModel.O1_MINI,
709
- OpenAIChatModel.O1_PREVIEW,
710
- ]
619
+ return (
620
+ self.is_openai_chat_model()
621
+ and get_model_info(self.config.chat_model).has_tools
622
+ )
711
623
 
712
624
  def is_openai_completion_model(self) -> bool:
713
625
  openai_completion_models = [e.value for e in OpenAICompletionModel]
@@ -726,40 +638,18 @@ class OpenAIGPT(LanguageModel):
726
638
  or self.chat_model_orig.startswith("deepseek/")
727
639
  )
728
640
 
729
- def requires_first_user_message(self) -> bool:
730
- """
731
- Does the chat_model require a non-empty first user message?
732
- TODO: Add other models here; we know gemini requires a non-empty
733
- user message, after the system message.
734
- """
735
- return self.is_gemini_model()
736
-
737
641
  def unsupported_params(self) -> List[str]:
738
642
  """
739
643
  List of params that are not supported by the current model
740
644
  """
741
- match self.chat_model_orig:
742
- case OpenAIChatModel.O1_MINI | OpenAIChatModel.O1_PREVIEW:
743
- return ["temperature", "stream"]
744
- case _:
745
- return []
645
+ return get_model_info(self.config.chat_model).unsupported_params
746
646
 
747
647
  def rename_params(self) -> Dict[str, str]:
748
648
  """
749
649
  Map of param name -> new name for specific models.
750
650
  Currently main troublemaker is o1* series.
751
651
  """
752
- match self.config.chat_model:
753
- case (
754
- OpenAIChatModel.O1_MINI
755
- | OpenAIChatModel.O1_PREVIEW
756
- | GeminiModel.GEMINI_1_5_FLASH
757
- | GeminiModel.GEMINI_1_5_FLASH_8B
758
- | GeminiModel.GEMINI_1_5_PRO
759
- ):
760
- return {"max_tokens": "max_completion_tokens"}
761
- case _:
762
- return {}
652
+ return get_model_info(self.config.chat_model).rename_params
763
653
 
764
654
  def chat_context_length(self) -> int:
765
655
  """
@@ -771,7 +661,7 @@ class OpenAIGPT(LanguageModel):
771
661
  if self.config.use_completion_for_chat
772
662
  else self.config.chat_model
773
663
  )
774
- return _context_length.get(model, super().chat_context_length())
664
+ return get_model_info(model).context_length
775
665
 
776
666
  def completion_context_length(self) -> int:
777
667
  """
@@ -783,7 +673,7 @@ class OpenAIGPT(LanguageModel):
783
673
  if self.config.use_chat_for_completion
784
674
  else self.config.completion_model
785
675
  )
786
- return _context_length.get(model, super().completion_context_length())
676
+ return get_model_info(model).context_length
787
677
 
788
678
  def chat_cost(self) -> Tuple[float, float]:
789
679
  """
@@ -791,7 +681,8 @@ class OpenAIGPT(LanguageModel):
791
681
  models/endpoints.
792
682
  Get it from the dict, otherwise fail-over to general method
793
683
  """
794
- return _cost_per_1k_tokens.get(self.chat_model_orig, super().chat_cost())
684
+ info = get_model_info(self.config.chat_model)
685
+ return (info.input_cost_per_million / 1000, info.output_cost_per_million / 1000)
795
686
 
796
687
  def set_stream(self, stream: bool) -> bool:
797
688
  """Enable or disable streaming output from API.
@@ -808,7 +699,7 @@ class OpenAIGPT(LanguageModel):
808
699
  return (
809
700
  self.config.stream
810
701
  and settings.stream
811
- and self.config.chat_model not in NON_STREAMING_MODELS
702
+ and get_model_info(self.config.chat_model).allows_streaming
812
703
  and not settings.quiet
813
704
  )
814
705
 
@@ -1795,7 +1686,7 @@ class OpenAIGPT(LanguageModel):
1795
1686
  and llm_messages[0].role == Role.SYSTEM
1796
1687
  # TODO: we will unconditionally insert a dummy user msg
1797
1688
  # if the only msg is a system msg.
1798
- # and self.requires_first_user_message()
1689
+ # We could make this conditional on ModelInfo.needs_first_user_message
1799
1690
  ):
1800
1691
  # some LLMs, notable Gemini as of 12/11/24,
1801
1692
  # require the first message to be from the user,
@@ -1813,8 +1704,9 @@ class OpenAIGPT(LanguageModel):
1813
1704
  model=chat_model,
1814
1705
  messages=[
1815
1706
  m.api_dict(
1816
- has_system_role=self.config.chat_model
1817
- not in NON_SYSTEM_MESSAGE_MODELS
1707
+ has_system_role=get_model_info(
1708
+ self.config.chat_model
1709
+ ).allows_system_message
1818
1710
  )
1819
1711
  for m in (llm_messages)
1820
1712
  ],
langroid/mytypes.py CHANGED
@@ -93,3 +93,12 @@ class Document(BaseModel):
93
93
  SOURCE:{self.metadata.source}
94
94
  """
95
95
  )
96
+
97
+
98
+ class NonToolAction(str, Enum):
99
+ """
100
+ Possible Routing options. Mainly used to handle non-tool msgs from LLM.
101
+ """
102
+
103
+ FORWARD_USER = "user" # forward msg to user
104
+ DONE = "done" # task done
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.38.0
3
+ Version: 0.39.1
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -1,11 +1,11 @@
1
1
  langroid/__init__.py,sha256=z_fCOLQJPOw3LLRPBlFB5-2HyCjpPgQa4m4iY5Fvb8Y,1800
2
2
  langroid/exceptions.py,sha256=OPjece_8cwg94DLPcOGA1ddzy5bGh65pxzcHMnssTz8,2995
3
- langroid/mytypes.py,sha256=h1eMq1ZwTLVezObPfCseWNWbEOzP7mAKu2XoS63W1cM,2647
3
+ langroid/mytypes.py,sha256=RUMSf2i-qE2L2A5Bigvi-1jL5MwsAFfG59rEEkcq7h0,2854
4
4
  langroid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
6
- langroid/agent/base.py,sha256=oThlrYygKDu1-bKjAfygldJ511gMKT8Z0qCrD52DdDM,77834
6
+ langroid/agent/base.py,sha256=CVPvy-bLI_6wHZxYf_spo4eq-utR373Ur7T3zKpe18U,78222
7
7
  langroid/agent/batch.py,sha256=vi1r5i1-vN80WfqHDSwjEym_KfGsqPGUtwktmiK1nuk,20635
8
- langroid/agent/chat_agent.py,sha256=_7vOhTauPpPiOih2hnec8hz0rytaxGN110ja9wRCLJ0,82276
8
+ langroid/agent/chat_agent.py,sha256=DQkYAWQoHH4uaBsF9n7JSbfpyPn7Sr16bdblzZPx78U,83573
9
9
  langroid/agent/chat_document.py,sha256=xzMtrPbaW-Y-BnF7kuhr2dorsD-D5rMWzfOqJ8HAoo8,17885
10
10
  langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
11
11
  langroid/agent/task.py,sha256=XrXUbSoiFasvpIsZPn_cBpdWaTCKljJPRimtLMrSZrs,90347
@@ -14,7 +14,7 @@ langroid/agent/xml_tool_message.py,sha256=6SshYZJKIfi4mkE-gIoSwjkEYekQ8GwcSiCv7a
14
14
  langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  langroid/agent/callbacks/chainlit.py,sha256=RH8qUXaZE5o2WQz3WJQ1SdFtASGlxWCA6_HYz_3meDQ,20822
16
16
  langroid/agent/special/__init__.py,sha256=gik_Xtm_zV7U9s30Mn8UX3Gyuy4jTjQe9zjiE3HWmEo,1273
17
- langroid/agent/special/doc_chat_agent.py,sha256=6Wz_i6lLwUcYIgaOrbP8oLebxHE0uAbo2_mbWXCT0-k,64686
17
+ langroid/agent/special/doc_chat_agent.py,sha256=tMx-3cBEIHJM14P20cYvIzAc9z-skSnHOPVJ0eegxzc,64692
18
18
  langroid/agent/special/lance_doc_chat_agent.py,sha256=s8xoRs0gGaFtDYFUSIRchsgDVbS5Q3C2b2mr3V1Fd-Q,10419
19
19
  langroid/agent/special/lance_tools.py,sha256=qS8x4wi8mrqfbYV2ztFzrcxyhHQ0ZWOc-zkYiH7awj0,2105
20
20
  langroid/agent/special/relevance_extractor_agent.py,sha256=zIx8GUdVo1aGW6ASla0NPQjYYIpmriK_TYMijqAx3F8,4796
@@ -64,12 +64,13 @@ langroid/embedding_models/protoc/embeddings.proto,sha256=_O-SgFpTaylQeOTgSpxhEJ7
64
64
  langroid/embedding_models/protoc/embeddings_pb2.py,sha256=4Q57PhOunv-uZNJrxYrWBXAI0ZtfnVZXFRhRj5JuRSg,1662
65
65
  langroid/embedding_models/protoc/embeddings_pb2.pyi,sha256=UkNy7BrNsmQm0vLb3NtGXy8jVtz-kPWwwFsX-QbQBhQ,1475
66
66
  langroid/embedding_models/protoc/embeddings_pb2_grpc.py,sha256=9dYQqkW3JPyBpSEjeGXTNpSqAkC-6FPtBHyteVob2Y8,2452
67
- langroid/language_models/__init__.py,sha256=ps8nhRavCu2-Bv7IQ5hrzody6lzKjHkivJsblDZZIQ8,1020
67
+ langroid/language_models/__init__.py,sha256=3aD2qC1lz8v12HX4B-dilv27gNxYdGdeu1QvDlkqqHs,1095
68
68
  langroid/language_models/azure_openai.py,sha256=zNQzzsERxNestq-hFfQZbvTzK43G2vjRWnTV3ktm1DQ,5845
69
- langroid/language_models/base.py,sha256=qxPcY-zBoP9xXVtytg4kfjTKLU9QOZLLAfDa_6skE94,24921
69
+ langroid/language_models/base.py,sha256=mN6HAjLgF2xpHObz5uPZ3JDID7jdTiRLEkoGgGrqLM8,25177
70
70
  langroid/language_models/config.py,sha256=9Q8wk5a7RQr8LGMT_0WkpjY8S4ywK06SalVRjXlfCiI,378
71
71
  langroid/language_models/mock_lm.py,sha256=5BgHKDVRWFbUwDT_PFgTZXz9-k8wJSA2e3PZmyDgQ1k,4022
72
- langroid/language_models/openai_gpt.py,sha256=yzkv9AFNL1iKdIQitB9mUqLjVwNREt8RWPEwVTwxDTk,80306
72
+ langroid/language_models/model_info.py,sha256=n60j5DrAlxCsHa8pl7PWNBPP7pSAEVMi2mUQ4JOqo7Q,10648
73
+ langroid/language_models/openai_gpt.py,sha256=Vuxz3tZThUN_9MF16KW0HJe0k0qk1tv58PZ0qqwpGdg,76754
73
74
  langroid/language_models/utils.py,sha256=L4_CbihDMTGcsg0TOG1Yd5JFEto46--h7CX_14m89sQ,5016
74
75
  langroid/language_models/prompt_formatter/__init__.py,sha256=2-5cdE24XoFDhifOLl8yiscohil1ogbP1ECkYdBlBsk,372
75
76
  langroid/language_models/prompt_formatter/base.py,sha256=eDS1sgRNZVnoajwV_ZIha6cba5Dt8xjgzdRbPITwx3Q,1221
@@ -123,7 +124,7 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
123
124
  langroid/vector_store/momento.py,sha256=xOaU7Hlyyn_5ihb0ARS5JHtmrKrTCt2IdRA-ioMM5ek,10307
124
125
  langroid/vector_store/qdrantdb.py,sha256=v7TAsIoj_vxeKDYS9tpwJLBZA8fuTweTYxHo0X_uawM,17949
125
126
  langroid/vector_store/weaviatedb.py,sha256=FOzgvqLqvdN5jJebVtJ-8tu2CeBzBfSP3ih4_ODEOOw,10605
126
- langroid-0.38.0.dist-info/METADATA,sha256=PMVjo4UfoNgUyP9RWp2AAEIWA9amAnTHUvzrPPjt_ms,60634
127
- langroid-0.38.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
128
- langroid-0.38.0.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
129
- langroid-0.38.0.dist-info/RECORD,,
127
+ langroid-0.39.1.dist-info/METADATA,sha256=4_I1_wyUNt5OCUpdKv2amypFdloe15-0pLndN-rwkew,60634
128
+ langroid-0.39.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
129
+ langroid-0.39.1.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
130
+ langroid-0.39.1.dist-info/RECORD,,