langroid 0.54.2__py3-none-any.whl → 0.55.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/done_sequence_parser.py +151 -0
- langroid/agent/task.py +261 -0
- {langroid-0.54.2.dist-info → langroid-0.55.0.dist-info}/METADATA +2 -2
- {langroid-0.54.2.dist-info → langroid-0.55.0.dist-info}/RECORD +6 -5
- {langroid-0.54.2.dist-info → langroid-0.55.0.dist-info}/WHEEL +0 -0
- {langroid-0.54.2.dist-info → langroid-0.55.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
"""Parser for done sequence DSL (Domain Specific Language).
|
2
|
+
|
3
|
+
Converts string patterns into DoneSequence objects for convenient task completion
|
4
|
+
configuration.
|
5
|
+
|
6
|
+
Examples:
|
7
|
+
"T, A" -> Tool followed by Agent response
|
8
|
+
"T[calculator], A" -> Specific tool 'calculator' followed by Agent response
|
9
|
+
"L, T, A, L" -> LLM, Tool, Agent, LLM sequence
|
10
|
+
"C[quit|exit]" -> Content matching regex pattern
|
11
|
+
"""
|
12
|
+
|
13
|
+
import re
|
14
|
+
from typing import List, Union
|
15
|
+
|
16
|
+
from langroid.pydantic_v1 import BaseModel
|
17
|
+
|
18
|
+
from .task import AgentEvent, DoneSequence, EventType
|
19
|
+
|
20
|
+
|
21
|
+
def parse_done_sequence(sequence: Union[str, DoneSequence]) -> DoneSequence:
|
22
|
+
"""Parse a string pattern or return existing DoneSequence unchanged.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
sequence: Either a DoneSequence object or a string pattern to parse
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
DoneSequence object
|
29
|
+
|
30
|
+
Raises:
|
31
|
+
ValueError: If the string pattern is invalid
|
32
|
+
"""
|
33
|
+
if isinstance(sequence, DoneSequence):
|
34
|
+
return sequence
|
35
|
+
|
36
|
+
if not isinstance(sequence, str):
|
37
|
+
raise ValueError(f"Expected string or DoneSequence, got {type(sequence)}")
|
38
|
+
|
39
|
+
events = _parse_string_pattern(sequence)
|
40
|
+
return DoneSequence(events=events)
|
41
|
+
|
42
|
+
|
43
|
+
def _parse_string_pattern(pattern: str) -> List[AgentEvent]:
|
44
|
+
"""Parse a string pattern into a list of AgentEvent objects.
|
45
|
+
|
46
|
+
Pattern format:
|
47
|
+
- Single letter codes: T, A, L, U, N, C
|
48
|
+
- Specific tools: T[tool_name]
|
49
|
+
- Content match: C[regex_pattern]
|
50
|
+
- Separated by commas, spaces allowed
|
51
|
+
|
52
|
+
Args:
|
53
|
+
pattern: String pattern to parse
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
List of AgentEvent objects
|
57
|
+
|
58
|
+
Raises:
|
59
|
+
ValueError: If pattern is invalid
|
60
|
+
"""
|
61
|
+
events = []
|
62
|
+
|
63
|
+
# Split by comma and strip whitespace
|
64
|
+
parts = [p.strip() for p in pattern.split(",")]
|
65
|
+
|
66
|
+
for part in parts:
|
67
|
+
if not part:
|
68
|
+
continue
|
69
|
+
|
70
|
+
event = _parse_event_token(part)
|
71
|
+
events.append(event)
|
72
|
+
|
73
|
+
if not events:
|
74
|
+
raise ValueError(f"No valid events found in pattern: {pattern}")
|
75
|
+
|
76
|
+
return events
|
77
|
+
|
78
|
+
|
79
|
+
def _parse_event_token(token: str) -> AgentEvent:
|
80
|
+
"""Parse a single event token into an AgentEvent.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
token: Single event token (e.g., "T", "T[calc]", "C[quit|exit]")
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
AgentEvent object
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
ValueError: If token is invalid
|
90
|
+
"""
|
91
|
+
# Check for bracket notation
|
92
|
+
bracket_match = re.match(r"^([A-Z])\[([^\]]+)\]$", token)
|
93
|
+
|
94
|
+
if bracket_match:
|
95
|
+
event_code = bracket_match.group(1)
|
96
|
+
param = bracket_match.group(2)
|
97
|
+
|
98
|
+
if event_code == "T":
|
99
|
+
# Specific tool: T[tool_name]
|
100
|
+
return AgentEvent(event_type=EventType.SPECIFIC_TOOL, tool_name=param)
|
101
|
+
elif event_code == "C":
|
102
|
+
# Content match: C[regex_pattern]
|
103
|
+
return AgentEvent(event_type=EventType.CONTENT_MATCH, content_pattern=param)
|
104
|
+
else:
|
105
|
+
raise ValueError(
|
106
|
+
f"Invalid event code with brackets: {event_code}. "
|
107
|
+
"Only T[tool] and C[pattern] are supported."
|
108
|
+
)
|
109
|
+
|
110
|
+
# Simple single-letter codes
|
111
|
+
event_map = {
|
112
|
+
"T": EventType.TOOL,
|
113
|
+
"A": EventType.AGENT_RESPONSE,
|
114
|
+
"L": EventType.LLM_RESPONSE,
|
115
|
+
"U": EventType.USER_RESPONSE,
|
116
|
+
"N": EventType.NO_RESPONSE,
|
117
|
+
"C": EventType.CONTENT_MATCH, # C without brackets matches any content
|
118
|
+
}
|
119
|
+
|
120
|
+
if token in event_map:
|
121
|
+
return AgentEvent(event_type=event_map[token])
|
122
|
+
|
123
|
+
# If not a single letter, could be a full event type name
|
124
|
+
token_upper = token.upper()
|
125
|
+
if token_upper == "TOOL":
|
126
|
+
return AgentEvent(event_type=EventType.TOOL)
|
127
|
+
elif token_upper == "AGENT":
|
128
|
+
return AgentEvent(event_type=EventType.AGENT_RESPONSE)
|
129
|
+
elif token_upper == "LLM":
|
130
|
+
return AgentEvent(event_type=EventType.LLM_RESPONSE)
|
131
|
+
elif token_upper == "USER":
|
132
|
+
return AgentEvent(event_type=EventType.USER_RESPONSE)
|
133
|
+
else:
|
134
|
+
raise ValueError(
|
135
|
+
f"Invalid event token: '{token}'. "
|
136
|
+
"Valid tokens are: T, A, L, U, N, C, or T[tool_name], C[pattern]"
|
137
|
+
)
|
138
|
+
|
139
|
+
|
140
|
+
def parse_done_sequences(
|
141
|
+
sequences: List[Union[str, DoneSequence]]
|
142
|
+
) -> List[DoneSequence]:
|
143
|
+
"""Parse a list of mixed string patterns and DoneSequence objects.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
sequences: List containing strings and/or DoneSequence objects
|
147
|
+
|
148
|
+
Returns:
|
149
|
+
List of DoneSequence objects
|
150
|
+
"""
|
151
|
+
return [parse_done_sequence(seq) for seq in sequences]
|
langroid/agent/task.py
CHANGED
@@ -6,6 +6,7 @@ import logging
|
|
6
6
|
import re
|
7
7
|
import threading
|
8
8
|
from collections import Counter, OrderedDict, deque
|
9
|
+
from enum import Enum
|
9
10
|
from pathlib import Path
|
10
11
|
from types import SimpleNamespace
|
11
12
|
from typing import (
|
@@ -20,6 +21,7 @@ from typing import (
|
|
20
21
|
Tuple,
|
21
22
|
Type,
|
22
23
|
TypeVar,
|
24
|
+
Union,
|
23
25
|
cast,
|
24
26
|
overload,
|
25
27
|
)
|
@@ -69,6 +71,38 @@ def noop_fn(*args: List[Any], **kwargs: Dict[str, Any]) -> None:
|
|
69
71
|
pass
|
70
72
|
|
71
73
|
|
74
|
+
class EventType(str, Enum):
|
75
|
+
"""Types of events that can occur in a task"""
|
76
|
+
|
77
|
+
TOOL = "tool" # Any tool generated
|
78
|
+
SPECIFIC_TOOL = "specific_tool" # Specific tool by name
|
79
|
+
LLM_RESPONSE = "llm_response" # LLM generates response
|
80
|
+
AGENT_RESPONSE = "agent_response" # Agent responds
|
81
|
+
USER_RESPONSE = "user_response" # User responds
|
82
|
+
CONTENT_MATCH = "content_match" # Response matches pattern
|
83
|
+
NO_RESPONSE = "no_response" # No valid response from entity
|
84
|
+
CUSTOM = "custom" # Custom condition
|
85
|
+
|
86
|
+
|
87
|
+
class AgentEvent(BaseModel):
|
88
|
+
"""Single event in a task sequence"""
|
89
|
+
|
90
|
+
event_type: EventType
|
91
|
+
tool_name: Optional[str] = None # For SPECIFIC_TOOL
|
92
|
+
content_pattern: Optional[str] = None # For CONTENT_MATCH (regex)
|
93
|
+
responder: Optional[str] = None # Specific responder name
|
94
|
+
# Optionally match only if the responder was specific entity/task
|
95
|
+
sender: Optional[str] = None # Entity name or Task name that sent the message
|
96
|
+
|
97
|
+
|
98
|
+
class DoneSequence(BaseModel):
|
99
|
+
"""A sequence of events that triggers task completion"""
|
100
|
+
|
101
|
+
events: List[AgentEvent]
|
102
|
+
# Optional name for debugging
|
103
|
+
name: Optional[str] = None
|
104
|
+
|
105
|
+
|
72
106
|
class TaskConfig(BaseModel):
|
73
107
|
"""Configuration for a Task. This is a container for any params that
|
74
108
|
we didn't include in the task `__init__` method.
|
@@ -108,6 +142,9 @@ class TaskConfig(BaseModel):
|
|
108
142
|
contains a Tool attempt by the LLM
|
109
143
|
(including tools not handled by the agent).
|
110
144
|
Default is False.
|
145
|
+
done_sequences (List[DoneSequence]): List of event sequences that trigger task
|
146
|
+
completion. Task is done if ANY sequence matches the recent event history.
|
147
|
+
Each sequence is checked against the message parent chain.
|
111
148
|
|
112
149
|
"""
|
113
150
|
|
@@ -120,6 +157,7 @@ class TaskConfig(BaseModel):
|
|
120
157
|
allow_subtask_multi_oai_tools: bool = True
|
121
158
|
recognize_string_signals: bool = True
|
122
159
|
done_if_tool: bool = False
|
160
|
+
done_sequences: Optional[List[Union[str, DoneSequence]]] = None
|
123
161
|
|
124
162
|
|
125
163
|
class Task:
|
@@ -257,6 +295,14 @@ class Task:
|
|
257
295
|
set_parent_agent=noop_fn,
|
258
296
|
)
|
259
297
|
self.config = config
|
298
|
+
# Store parsed done sequences
|
299
|
+
self._parsed_done_sequences: Optional[List[DoneSequence]] = None
|
300
|
+
if self.config.done_sequences:
|
301
|
+
from .done_sequence_parser import parse_done_sequences
|
302
|
+
|
303
|
+
self._parsed_done_sequences = parse_done_sequences(
|
304
|
+
self.config.done_sequences
|
305
|
+
)
|
260
306
|
# how to behave as a sub-task; can be overridden by `add_sub_task()`
|
261
307
|
self.config_sub_task = copy.deepcopy(config)
|
262
308
|
# counts of distinct pending messages in history,
|
@@ -360,6 +406,8 @@ class Task:
|
|
360
406
|
self.single_round = single_round
|
361
407
|
self.turns = -1 # no limit
|
362
408
|
self.llm_delegate = llm_delegate
|
409
|
+
# Track last responder for done sequence checking
|
410
|
+
self._last_responder: Optional[Responder] = None
|
363
411
|
if llm_delegate:
|
364
412
|
if self.single_round:
|
365
413
|
# 0: User instructs (delegating to LLM);
|
@@ -1276,6 +1324,9 @@ class Task:
|
|
1276
1324
|
|
1277
1325
|
self._update_no_answer_vars(result)
|
1278
1326
|
|
1327
|
+
# Store the last responder for done sequence checking
|
1328
|
+
self._last_responder = r
|
1329
|
+
|
1279
1330
|
# pending_sender is of type Responder,
|
1280
1331
|
# i.e. it is either one of the agent's entities
|
1281
1332
|
# OR a sub-task, that has produced a valid response.
|
@@ -1841,6 +1892,23 @@ class Task:
|
|
1841
1892
|
):
|
1842
1893
|
return (True, StatusCode.DONE)
|
1843
1894
|
|
1895
|
+
# Check done sequences
|
1896
|
+
if self._parsed_done_sequences and result is not None:
|
1897
|
+
# Get the message chain from the current result
|
1898
|
+
msg_chain = self._get_message_chain(result)
|
1899
|
+
|
1900
|
+
# Use last responder if r not provided
|
1901
|
+
responder = r if r is not None else self._last_responder
|
1902
|
+
|
1903
|
+
# Check each sequence
|
1904
|
+
for sequence in self._parsed_done_sequences:
|
1905
|
+
if self._matches_sequence_with_current(
|
1906
|
+
msg_chain, sequence, result, responder
|
1907
|
+
):
|
1908
|
+
seq_name = sequence.name or "unnamed"
|
1909
|
+
logger.info(f"Task {self.name} done: matched sequence '{seq_name}'")
|
1910
|
+
return (True, StatusCode.DONE)
|
1911
|
+
|
1844
1912
|
allow_done_string = self.config.recognize_string_signals
|
1845
1913
|
# An entity decided task is done, either via DoneTool,
|
1846
1914
|
# or by explicitly saying DONE
|
@@ -2127,3 +2195,196 @@ class Task:
|
|
2127
2195
|
return False, addressee, content_to_send
|
2128
2196
|
|
2129
2197
|
return None, None, None
|
2198
|
+
|
2199
|
+
def _classify_event(
|
2200
|
+
self, msg: ChatDocument | None, responder: Responder | None
|
2201
|
+
) -> Optional[AgentEvent]:
|
2202
|
+
"""Classify a message into an AgentEvent for sequence matching."""
|
2203
|
+
if msg is None:
|
2204
|
+
return AgentEvent(event_type=EventType.NO_RESPONSE)
|
2205
|
+
|
2206
|
+
# Determine the event type based on responder and message content
|
2207
|
+
event_type = EventType.NO_RESPONSE
|
2208
|
+
tool_name = None
|
2209
|
+
|
2210
|
+
# Check if there are tool messages
|
2211
|
+
tool_messages = self.agent.try_get_tool_messages(msg, all_tools=True)
|
2212
|
+
if tool_messages:
|
2213
|
+
event_type = EventType.TOOL
|
2214
|
+
if len(tool_messages) == 1:
|
2215
|
+
tool_name = tool_messages[0].request
|
2216
|
+
|
2217
|
+
# Check responder type
|
2218
|
+
if responder == Entity.LLM and not tool_messages:
|
2219
|
+
event_type = EventType.LLM_RESPONSE
|
2220
|
+
elif responder == Entity.AGENT:
|
2221
|
+
event_type = EventType.AGENT_RESPONSE
|
2222
|
+
elif responder == Entity.USER:
|
2223
|
+
event_type = EventType.USER_RESPONSE
|
2224
|
+
elif isinstance(responder, Task):
|
2225
|
+
# For sub-task responses, check the sender in metadata
|
2226
|
+
if msg.metadata.sender == Entity.LLM:
|
2227
|
+
event_type = EventType.LLM_RESPONSE
|
2228
|
+
elif msg.metadata.sender == Entity.AGENT:
|
2229
|
+
event_type = EventType.AGENT_RESPONSE
|
2230
|
+
else:
|
2231
|
+
event_type = EventType.USER_RESPONSE
|
2232
|
+
|
2233
|
+
# Get sender name
|
2234
|
+
sender_name = None
|
2235
|
+
if isinstance(responder, Entity):
|
2236
|
+
sender_name = responder.value
|
2237
|
+
elif isinstance(responder, Task):
|
2238
|
+
sender_name = responder.name
|
2239
|
+
|
2240
|
+
return AgentEvent(
|
2241
|
+
event_type=event_type,
|
2242
|
+
tool_name=tool_name,
|
2243
|
+
sender=sender_name,
|
2244
|
+
)
|
2245
|
+
|
2246
|
+
def _get_message_chain(
|
2247
|
+
self, msg: ChatDocument | None, max_depth: Optional[int] = None
|
2248
|
+
) -> List[ChatDocument]:
|
2249
|
+
"""Get the chain of messages by following parent pointers."""
|
2250
|
+
if max_depth is None:
|
2251
|
+
# Get max depth needed from all sequences
|
2252
|
+
max_depth = 50 # default fallback
|
2253
|
+
if self._parsed_done_sequences:
|
2254
|
+
max_depth = max(len(seq.events) for seq in self._parsed_done_sequences)
|
2255
|
+
|
2256
|
+
chain = []
|
2257
|
+
current = msg
|
2258
|
+
depth = 0
|
2259
|
+
|
2260
|
+
while current is not None and depth < max_depth:
|
2261
|
+
chain.append(current)
|
2262
|
+
current = current.parent
|
2263
|
+
depth += 1
|
2264
|
+
|
2265
|
+
# Reverse to get chronological order (oldest first)
|
2266
|
+
return list(reversed(chain))
|
2267
|
+
|
2268
|
+
def _matches_event(self, actual: AgentEvent, expected: AgentEvent) -> bool:
|
2269
|
+
"""Check if an actual event matches an expected event pattern."""
|
2270
|
+
# Check event type
|
2271
|
+
if expected.event_type == EventType.SPECIFIC_TOOL:
|
2272
|
+
if actual.event_type != EventType.TOOL:
|
2273
|
+
return False
|
2274
|
+
if expected.tool_name and actual.tool_name != expected.tool_name:
|
2275
|
+
return False
|
2276
|
+
elif actual.event_type != expected.event_type:
|
2277
|
+
return False
|
2278
|
+
|
2279
|
+
# Check sender if specified
|
2280
|
+
if expected.sender and actual.sender != expected.sender:
|
2281
|
+
return False
|
2282
|
+
|
2283
|
+
# TODO: Add content pattern matching for CONTENT_MATCH type
|
2284
|
+
|
2285
|
+
return True
|
2286
|
+
|
2287
|
+
def _matches_sequence(
|
2288
|
+
self, msg_chain: List[ChatDocument], sequence: DoneSequence
|
2289
|
+
) -> bool:
|
2290
|
+
"""Check if a message chain matches a done sequence.
|
2291
|
+
|
2292
|
+
We traverse the message chain and try to match the sequence events.
|
2293
|
+
The events don't have to be consecutive in the chain.
|
2294
|
+
"""
|
2295
|
+
if not sequence.events:
|
2296
|
+
return False
|
2297
|
+
|
2298
|
+
# Convert messages to events
|
2299
|
+
events = []
|
2300
|
+
for i, msg in enumerate(msg_chain):
|
2301
|
+
# Determine responder from metadata or by checking previous message
|
2302
|
+
responder = None
|
2303
|
+
if msg.metadata.sender:
|
2304
|
+
responder = msg.metadata.sender
|
2305
|
+
elif msg.metadata.sender_name:
|
2306
|
+
# Could be a task name - keep as None for now since we can't resolve
|
2307
|
+
# the actual Task object from just the name
|
2308
|
+
responder = None
|
2309
|
+
|
2310
|
+
event = self._classify_event(msg, responder)
|
2311
|
+
if event:
|
2312
|
+
events.append(event)
|
2313
|
+
|
2314
|
+
# Try to match the sequence
|
2315
|
+
seq_idx = 0
|
2316
|
+
for event in events:
|
2317
|
+
if seq_idx >= len(sequence.events):
|
2318
|
+
break
|
2319
|
+
|
2320
|
+
expected = sequence.events[seq_idx]
|
2321
|
+
if self._matches_event(event, expected):
|
2322
|
+
seq_idx += 1
|
2323
|
+
|
2324
|
+
# Check if we matched the entire sequence
|
2325
|
+
return seq_idx == len(sequence.events)
|
2326
|
+
|
2327
|
+
def _matches_sequence_with_current(
|
2328
|
+
self,
|
2329
|
+
msg_chain: List[ChatDocument],
|
2330
|
+
sequence: DoneSequence,
|
2331
|
+
current_msg: ChatDocument,
|
2332
|
+
current_responder: Optional[Responder],
|
2333
|
+
) -> bool:
|
2334
|
+
"""Check if the message chain plus current message matches a done sequence.
|
2335
|
+
|
2336
|
+
Process messages in reverse order (newest first) and match against
|
2337
|
+
the sequence events in reverse order.
|
2338
|
+
"""
|
2339
|
+
# Add current message to chain if not already there
|
2340
|
+
if not msg_chain or msg_chain[-1].id() != current_msg.id():
|
2341
|
+
msg_chain = msg_chain + [current_msg]
|
2342
|
+
|
2343
|
+
# If we don't have enough messages for the sequence, can't match
|
2344
|
+
if len(msg_chain) < len(sequence.events):
|
2345
|
+
return False
|
2346
|
+
|
2347
|
+
# Process in reverse order - start from the end of both lists
|
2348
|
+
seq_idx = len(sequence.events) - 1
|
2349
|
+
msg_idx = len(msg_chain) - 1
|
2350
|
+
|
2351
|
+
while seq_idx >= 0 and msg_idx >= 0:
|
2352
|
+
msg = msg_chain[msg_idx]
|
2353
|
+
expected = sequence.events[seq_idx]
|
2354
|
+
|
2355
|
+
# Determine responder for this message
|
2356
|
+
if msg_idx == len(msg_chain) - 1 and current_responder is not None:
|
2357
|
+
# For the last message, use the current responder
|
2358
|
+
responder = current_responder
|
2359
|
+
else:
|
2360
|
+
# For other messages, determine from metadata
|
2361
|
+
responder = msg.metadata.sender
|
2362
|
+
|
2363
|
+
# Classify the event
|
2364
|
+
event = self._classify_event(msg, responder)
|
2365
|
+
if not event:
|
2366
|
+
return False
|
2367
|
+
|
2368
|
+
# Check if it matches
|
2369
|
+
matched = False
|
2370
|
+
|
2371
|
+
# Special handling for CONTENT_MATCH
|
2372
|
+
if (
|
2373
|
+
expected.event_type == EventType.CONTENT_MATCH
|
2374
|
+
and expected.content_pattern
|
2375
|
+
):
|
2376
|
+
if re.search(expected.content_pattern, msg.content, re.IGNORECASE):
|
2377
|
+
matched = True
|
2378
|
+
elif self._matches_event(event, expected):
|
2379
|
+
matched = True
|
2380
|
+
|
2381
|
+
if not matched:
|
2382
|
+
# Strict matching - no skipping allowed
|
2383
|
+
return False
|
2384
|
+
else:
|
2385
|
+
# Matched! Move to next expected event
|
2386
|
+
seq_idx -= 1
|
2387
|
+
msg_idx -= 1
|
2388
|
+
|
2389
|
+
# We matched if we've matched all events in the sequence
|
2390
|
+
return seq_idx < 0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.55.0
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
Author-email: Prasad Chalasani <pchalasani@gmail.com>
|
6
6
|
License: MIT
|
@@ -209,7 +209,7 @@ Description-Content-Type: text/markdown
|
|
209
209
|
[](https://pypi.org/project/langroid/)
|
210
210
|
[](https://pypi.org/project/langroid/)
|
211
211
|
[](https://github.com/langroid/langroid/actions/workflows/pytest.yml)
|
212
|
-
[](https://codecov.io/gh/langroid/langroid)
|
213
213
|
[](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml)
|
214
214
|
|
215
215
|
[](https://langroid.github.io/langroid)
|
@@ -7,8 +7,9 @@ langroid/agent/base.py,sha256=a45iqoWet2W60h4wUIOyHDYlotgS0asqIjfbONT4fZQ,85706
|
|
7
7
|
langroid/agent/batch.py,sha256=wpE9RqCNDVDhAXkCB7wEqfCIEAi6qKcrhaZ-Zr9T4C0,21375
|
8
8
|
langroid/agent/chat_agent.py,sha256=2HIYzYxkrGkRIS97ioKfIqjaW3RbX89M39LjzBobBEY,88381
|
9
9
|
langroid/agent/chat_document.py,sha256=6O20Fp4QrquykaF2jFtwNHkvcoDte1LLwVZNk9mVH9c,18057
|
10
|
+
langroid/agent/done_sequence_parser.py,sha256=GiLTVtzzKb_YQcPpwqOajKu97RSUzZC6ae_ZagDmt_A,4425
|
10
11
|
langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
|
11
|
-
langroid/agent/task.py,sha256=
|
12
|
+
langroid/agent/task.py,sha256=1xjey94vQRDDmXnhYCVu5D6Nw-3dVUaf_bUmXzRKxIk,101213
|
12
13
|
langroid/agent/tool_message.py,sha256=BhjP-_TfQ2tgxuY4Yo_JHLOwwt0mJ4BwjPnREvEY4vk,14744
|
13
14
|
langroid/agent/xml_tool_message.py,sha256=oeBKnJNoGaKdtz39XoWGMTNlVyXew2MWH5lgtYeh8wQ,15496
|
14
15
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -133,7 +134,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
|
|
133
134
|
langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
|
134
135
|
langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
|
135
136
|
langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
|
136
|
-
langroid-0.
|
137
|
-
langroid-0.
|
138
|
-
langroid-0.
|
139
|
-
langroid-0.
|
137
|
+
langroid-0.55.0.dist-info/METADATA,sha256=5RQdMjfiolUraCednhoHvcQVOaSHcOqxHj5e-JS3M6Q,65366
|
138
|
+
langroid-0.55.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
139
|
+
langroid-0.55.0.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
140
|
+
langroid-0.55.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|