lionagi 0.6.0__py3-none-any.whl → 0.7.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.
- lionagi/__init__.py +2 -0
- lionagi/libs/token_transform/__init__.py +0 -0
- lionagi/libs/token_transform/llmlingua.py +1 -0
- lionagi/libs/token_transform/perplexity.py +439 -0
- lionagi/libs/token_transform/synthlang.py +409 -0
- lionagi/operations/ReAct/ReAct.py +126 -0
- lionagi/operations/ReAct/utils.py +28 -0
- lionagi/operations/__init__.py +1 -9
- lionagi/operations/_act/act.py +73 -0
- lionagi/operations/chat/__init__.py +3 -0
- lionagi/operations/chat/chat.py +173 -0
- lionagi/operations/communicate/__init__.py +0 -0
- lionagi/operations/communicate/communicate.py +167 -0
- lionagi/operations/instruct/__init__.py +3 -0
- lionagi/operations/instruct/instruct.py +29 -0
- lionagi/operations/interpret/__init__.py +3 -0
- lionagi/operations/interpret/interpret.py +40 -0
- lionagi/operations/operate/__init__.py +3 -0
- lionagi/operations/operate/operate.py +189 -0
- lionagi/operations/parse/__init__.py +3 -0
- lionagi/operations/parse/parse.py +125 -0
- lionagi/operations/plan/plan.py +3 -3
- lionagi/operations/select/__init__.py +0 -4
- lionagi/operations/select/select.py +11 -30
- lionagi/operations/select/utils.py +13 -2
- lionagi/operations/translate/__init__.py +0 -0
- lionagi/operations/translate/translate.py +47 -0
- lionagi/operations/types.py +25 -3
- lionagi/operatives/action/function_calling.py +1 -1
- lionagi/operatives/action/manager.py +22 -26
- lionagi/operatives/action/tool.py +1 -1
- lionagi/operatives/strategies/__init__.py +3 -0
- lionagi/{operations → operatives}/strategies/params.py +18 -2
- lionagi/protocols/adapters/__init__.py +0 -0
- lionagi/protocols/adapters/adapter.py +95 -0
- lionagi/protocols/adapters/json_adapter.py +101 -0
- lionagi/protocols/adapters/pandas_/__init__.py +0 -0
- lionagi/protocols/adapters/pandas_/csv_adapter.py +50 -0
- lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -0
- lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +31 -0
- lionagi/protocols/adapters/pandas_/pd_series_adapter.py +17 -0
- lionagi/protocols/adapters/types.py +18 -0
- lionagi/protocols/generic/pile.py +22 -1
- lionagi/protocols/graph/node.py +17 -1
- lionagi/protocols/types.py +3 -3
- lionagi/service/__init__.py +1 -14
- lionagi/service/endpoints/base.py +1 -1
- lionagi/service/endpoints/rate_limited_processor.py +2 -1
- lionagi/service/manager.py +1 -1
- lionagi/service/types.py +18 -0
- lionagi/session/branch.py +1098 -929
- lionagi/version.py +1 -1
- {lionagi-0.6.0.dist-info → lionagi-0.7.0.dist-info}/METADATA +4 -4
- {lionagi-0.6.0.dist-info → lionagi-0.7.0.dist-info}/RECORD +66 -38
- lionagi/libs/compress/models.py +0 -66
- lionagi/libs/compress/utils.py +0 -69
- lionagi/operations/select/prompt.py +0 -5
- lionagi/protocols/_adapter.py +0 -224
- /lionagi/{libs/compress → operations/ReAct}/__init__.py +0 -0
- /lionagi/operations/{strategies → _act}/__init__.py +0 -0
- /lionagi/{operations → operatives}/strategies/base.py +0 -0
- /lionagi/{operations → operatives}/strategies/concurrent.py +0 -0
- /lionagi/{operations → operatives}/strategies/concurrent_chunk.py +0 -0
- /lionagi/{operations → operatives}/strategies/concurrent_sequential_chunk.py +0 -0
- /lionagi/{operations → operatives}/strategies/sequential.py +0 -0
- /lionagi/{operations → operatives}/strategies/sequential_chunk.py +0 -0
- /lionagi/{operations → operatives}/strategies/sequential_concurrent_chunk.py +0 -0
- /lionagi/{operations → operatives}/strategies/utils.py +0 -0
- {lionagi-0.6.0.dist-info → lionagi-0.7.0.dist-info}/WHEEL +0 -0
- {lionagi-0.6.0.dist-info → lionagi-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,409 @@
|
|
1
|
+
# synthlang.py
|
2
|
+
"""
|
3
|
+
SynthLang translator and system prompt generator.
|
4
|
+
We incorporate the simpler framework approach (a list of frameworks to enable),
|
5
|
+
and rely on a chosen template from the included FRAMEWORK_TEMPLATES.
|
6
|
+
We also integrate optional perplexity compression from `compress_perplexity.py`.
|
7
|
+
"""
|
8
|
+
# For the optional compression step:
|
9
|
+
from timeit import default_timer as timer
|
10
|
+
from typing import Literal
|
11
|
+
|
12
|
+
from lionagi.session.branch import Branch
|
13
|
+
from lionagi.settings import Settings
|
14
|
+
|
15
|
+
from .perplexity import compress_text, iModel
|
16
|
+
|
17
|
+
########################################################################
|
18
|
+
# 1) Template Data
|
19
|
+
# Each item is a dictionary capturing your multi-line text blocks.
|
20
|
+
########################################################################
|
21
|
+
|
22
|
+
group_theory = {
|
23
|
+
"title": "Group Theory Analysis",
|
24
|
+
"domain": "mathematics",
|
25
|
+
"category": "Abstract Algebra",
|
26
|
+
"overview": "Investigation of algebraic structures using group theory and ring theory.",
|
27
|
+
"content": """# Structures
|
28
|
+
Let G be a group with operation •
|
29
|
+
Let H be a subgroup of G
|
30
|
+
Let N be a normal subgroup of G
|
31
|
+
|
32
|
+
# Properties
|
33
|
+
P(x): "x is a homomorphism"
|
34
|
+
Q(x): "x preserves group structure"
|
35
|
+
R(x): "x maps to kernel"
|
36
|
+
|
37
|
+
# Objectives
|
38
|
+
1. Prove fundamental homomorphism theorem
|
39
|
+
2. Show group action properties
|
40
|
+
3. Analyze quotient groups
|
41
|
+
""",
|
42
|
+
}
|
43
|
+
|
44
|
+
category_theory = {
|
45
|
+
"title": "Category Theory Concepts",
|
46
|
+
"domain": "mathematics",
|
47
|
+
"category": "Category Theory",
|
48
|
+
"overview": "Abstract mathematical framework dealing with relationships between structures.",
|
49
|
+
"content": """# Basic Definitions
|
50
|
+
C = (Ob(C), hom(C), ∘)
|
51
|
+
F: C → D (Functor)
|
52
|
+
...
|
53
|
+
""",
|
54
|
+
}
|
55
|
+
|
56
|
+
complex_analysis = {
|
57
|
+
"title": "Complex Analysis Foundations",
|
58
|
+
"domain": "mathematics",
|
59
|
+
"category": "Complex Analysis",
|
60
|
+
"overview": "Advanced study of complex functions, including integration and residue theory.",
|
61
|
+
"content": """# Complex Integration
|
62
|
+
∮ f(z)dz = 2πi∑Res(f,ak)
|
63
|
+
...
|
64
|
+
""",
|
65
|
+
}
|
66
|
+
|
67
|
+
math_logic = {
|
68
|
+
"title": "Mathematical Logic Foundations",
|
69
|
+
"domain": "mathematics",
|
70
|
+
"category": "Mathematical Logic",
|
71
|
+
"overview": "Exploration of fundamental mathematical logic concepts.",
|
72
|
+
"content": """# Sets and Axioms
|
73
|
+
Let A be the set of all propositions.
|
74
|
+
...
|
75
|
+
""",
|
76
|
+
}
|
77
|
+
|
78
|
+
number_theory = {
|
79
|
+
"title": "Number Theory Principles",
|
80
|
+
"domain": "mathematics",
|
81
|
+
"category": "Number Theory",
|
82
|
+
"overview": "Study of integers, prime numbers, and arithmetic functions.",
|
83
|
+
"content": """# Congruence Relations
|
84
|
+
a ≡ b (mod n)
|
85
|
+
...
|
86
|
+
""",
|
87
|
+
}
|
88
|
+
|
89
|
+
set_theory = {
|
90
|
+
"title": "Set Theory Foundations",
|
91
|
+
"domain": "mathematics",
|
92
|
+
"category": "Set Theory",
|
93
|
+
"overview": "Exploration of fundamental set theory concepts and operations.",
|
94
|
+
"content": """# Basic Definitions
|
95
|
+
Let U be the universal set
|
96
|
+
...
|
97
|
+
""",
|
98
|
+
}
|
99
|
+
|
100
|
+
symbolic_systems = {
|
101
|
+
"title": "Symbolic Systems Analysis",
|
102
|
+
"domain": "computer-science",
|
103
|
+
"category": "Symbolic Systems",
|
104
|
+
"overview": "Analysis of symbolic computation in AI and formal languages.",
|
105
|
+
"content": """# Core Concepts
|
106
|
+
Let Σ be a finite alphabet
|
107
|
+
...
|
108
|
+
""",
|
109
|
+
}
|
110
|
+
|
111
|
+
symbolic_exploration_system_supression = {
|
112
|
+
"title": "Systematic Suppression Exploration",
|
113
|
+
"domain": "societal",
|
114
|
+
"category": "Real-World Simulations",
|
115
|
+
"overview": "Analysis of suppression mechanisms using symbolic logic.",
|
116
|
+
"content": """# Sets and Categories
|
117
|
+
Let U be the set of actions representing policies ...
|
118
|
+
""",
|
119
|
+
}
|
120
|
+
|
121
|
+
topology_fundamentals = {
|
122
|
+
"title": "Topology Fundamentals",
|
123
|
+
"domain": "mathematics",
|
124
|
+
"category": "Topology",
|
125
|
+
"overview": "Study of geometric properties under continuous deformations.",
|
126
|
+
"content": """# Open Sets
|
127
|
+
(X,τ) topological space
|
128
|
+
...
|
129
|
+
""",
|
130
|
+
}
|
131
|
+
|
132
|
+
FRAMEWORK_TEMPLATES = {
|
133
|
+
"group_theory": group_theory,
|
134
|
+
"category_theory": category_theory,
|
135
|
+
"complex_analysis": complex_analysis,
|
136
|
+
"math_logic": math_logic,
|
137
|
+
"number_theory": number_theory,
|
138
|
+
"set_theory": set_theory,
|
139
|
+
"symbolic_systems": symbolic_systems,
|
140
|
+
"symbolic_exploration_system_supression": symbolic_exploration_system_supression,
|
141
|
+
"topology_fundamentals": topology_fundamentals,
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
########################################################################
|
146
|
+
# 2) Framework Definitions (Simplified)
|
147
|
+
########################################################################
|
148
|
+
|
149
|
+
# Instead of a complex dict with "selectedGlyphs", let's do a simpler approach:
|
150
|
+
# We define each "framework" by name, description, and glyphs. The user can pick them by name.
|
151
|
+
FRAMEWORK_OPTIONS = {
|
152
|
+
"math": {
|
153
|
+
"name": "Mathematical Framework",
|
154
|
+
"description": "Offers a suite of math glyphs and notation rules.",
|
155
|
+
"glyphs": [
|
156
|
+
{
|
157
|
+
"symbol": "↹",
|
158
|
+
"name": "Focus/Filter",
|
159
|
+
"description": "Used for focusing instructions",
|
160
|
+
},
|
161
|
+
{
|
162
|
+
"symbol": "Σ",
|
163
|
+
"name": "Summarize",
|
164
|
+
"description": "Condense large sets of data",
|
165
|
+
},
|
166
|
+
{
|
167
|
+
"symbol": "⊕",
|
168
|
+
"name": "Combine/Merge",
|
169
|
+
"description": "Merge multiple data sources",
|
170
|
+
},
|
171
|
+
{
|
172
|
+
"symbol": "•",
|
173
|
+
"name": "Group Operation",
|
174
|
+
"description": "Binary operation in group theory",
|
175
|
+
},
|
176
|
+
],
|
177
|
+
},
|
178
|
+
"optim": {
|
179
|
+
"name": "Optimization Framework",
|
180
|
+
"description": "Compression and optimization for code/math expressions.",
|
181
|
+
"glyphs": [
|
182
|
+
{
|
183
|
+
"symbol": "IF",
|
184
|
+
"name": "Conditional Operator",
|
185
|
+
"description": "Represents branching logic",
|
186
|
+
}
|
187
|
+
],
|
188
|
+
},
|
189
|
+
"custom_algebra": {
|
190
|
+
"name": "Custom Algebraic Framework",
|
191
|
+
"description": "Extra rules for ring, group, field expansions.",
|
192
|
+
"glyphs": [
|
193
|
+
{
|
194
|
+
"symbol": "∞",
|
195
|
+
"name": "Infinite Operator",
|
196
|
+
"description": "Represents unbounded algebraic ops",
|
197
|
+
}
|
198
|
+
],
|
199
|
+
},
|
200
|
+
}
|
201
|
+
|
202
|
+
########################################################################
|
203
|
+
# 3) SynthLang Base Prompt
|
204
|
+
########################################################################
|
205
|
+
|
206
|
+
BASE_PROMPT = """You are a SynthLang translator. You convert standard text into SynthLang format with these rules:
|
207
|
+
|
208
|
+
[Framework Integration]
|
209
|
+
- Use relevant framework glyphs wherever possible.
|
210
|
+
- Combine multiple frameworks if requested.
|
211
|
+
|
212
|
+
[Grammar Rules]
|
213
|
+
1) Task Glyphs:
|
214
|
+
- ↹ (Focus/Filter) for main tasks
|
215
|
+
- Σ (Summarize) for condensing info
|
216
|
+
- ⊕ (Combine) for merging data
|
217
|
+
- ? (Query) for clarifications
|
218
|
+
- IF for conditionals
|
219
|
+
|
220
|
+
2) Subject Markers:
|
221
|
+
- Use • before datasets (e.g., •myData)
|
222
|
+
- Use 花 for abstract concepts
|
223
|
+
- Use 山 for hierarchical structures
|
224
|
+
|
225
|
+
3) Modifiers:
|
226
|
+
- ^format(type) for output format
|
227
|
+
- ^n for importance level
|
228
|
+
- ^lang for language
|
229
|
+
- ^t{n} for time constraints
|
230
|
+
|
231
|
+
4) Flow Control:
|
232
|
+
- [p=n] for priority
|
233
|
+
- -> for sequential
|
234
|
+
- + for parallel
|
235
|
+
- | for alternatives
|
236
|
+
|
237
|
+
[Output Style]
|
238
|
+
- Maximize compression by removing articles & filler
|
239
|
+
- Use glyphs for repeated phrases
|
240
|
+
- Preserve semantic meaning
|
241
|
+
"""
|
242
|
+
|
243
|
+
|
244
|
+
########################################################################
|
245
|
+
# 4) Utility to build the "Active Frameworks" block
|
246
|
+
########################################################################
|
247
|
+
|
248
|
+
|
249
|
+
def build_frameworks_text(frameworks: list[str]) -> str:
|
250
|
+
"""
|
251
|
+
Takes a list of framework keys (e.g. ["math", "optim"]) and returns
|
252
|
+
a textual block describing them.
|
253
|
+
"""
|
254
|
+
lines = []
|
255
|
+
if not frameworks:
|
256
|
+
frameworks = FRAMEWORK_OPTIONS.keys()
|
257
|
+
|
258
|
+
for fw_key in frameworks:
|
259
|
+
fw = FRAMEWORK_OPTIONS.get(fw_key, None)
|
260
|
+
if fw:
|
261
|
+
lines.append(f"{fw['name']}: {fw['description']}")
|
262
|
+
lines.append("Glyphs:")
|
263
|
+
for g in fw["glyphs"]:
|
264
|
+
lines.append(
|
265
|
+
f" {g['symbol']} -> {g['name']} ({g['description']})"
|
266
|
+
)
|
267
|
+
lines.append("")
|
268
|
+
return "\n".join(lines).strip()
|
269
|
+
|
270
|
+
|
271
|
+
########################################################################
|
272
|
+
# 5) Main: create the SynthLang prompt
|
273
|
+
########################################################################
|
274
|
+
|
275
|
+
|
276
|
+
def create_synthlang_system_prompt(
|
277
|
+
template_name: Literal[
|
278
|
+
"group_theory",
|
279
|
+
"category_theory",
|
280
|
+
"complex_analysis",
|
281
|
+
"math_logic",
|
282
|
+
"number_theory",
|
283
|
+
"set_theory",
|
284
|
+
"symbolic_systems",
|
285
|
+
"symbolic_exploration_system_supression",
|
286
|
+
"topology_fundamentals",
|
287
|
+
],
|
288
|
+
frameworks: list[str],
|
289
|
+
additional_text: str = "",
|
290
|
+
) -> str:
|
291
|
+
"""
|
292
|
+
Merges:
|
293
|
+
- BASE_PROMPT
|
294
|
+
- "Active Frameworks" block (from frameworks list)
|
295
|
+
- The chosen template (from FRAMEWORK_TEMPLATES)
|
296
|
+
- plus additional text if desired
|
297
|
+
"""
|
298
|
+
template_block = FRAMEWORK_TEMPLATES[template_name]
|
299
|
+
frameworks_text = build_frameworks_text(frameworks)
|
300
|
+
|
301
|
+
# We show the user the "domain", "category", etc. from the template
|
302
|
+
template_details = (
|
303
|
+
f"Title: {template_block['title']}\n"
|
304
|
+
f"Domain: {template_block['domain']}\n"
|
305
|
+
f"Category: {template_block['category']}\n"
|
306
|
+
f"Overview: {template_block['overview']}\n"
|
307
|
+
"Excerpt:\n"
|
308
|
+
f"{template_block['content']}\n"
|
309
|
+
)
|
310
|
+
|
311
|
+
prompt = (
|
312
|
+
f"{BASE_PROMPT}\n\n"
|
313
|
+
"[Active Frameworks]\n"
|
314
|
+
f"{frameworks_text}\n\n"
|
315
|
+
"[Template]\n"
|
316
|
+
f"{template_details}\n"
|
317
|
+
)
|
318
|
+
|
319
|
+
if additional_text.strip():
|
320
|
+
prompt += f"\n[Additional]\n{additional_text.strip()}\n"
|
321
|
+
|
322
|
+
# Usually you might want the entire final text to be the system instruction:
|
323
|
+
return prompt
|
324
|
+
|
325
|
+
|
326
|
+
########################################################################
|
327
|
+
# 6) Translate text into SynthLang format
|
328
|
+
########################################################################
|
329
|
+
|
330
|
+
|
331
|
+
# TODO: refactor using SynthLang package
|
332
|
+
async def translate_to_synthlang(
|
333
|
+
text: str,
|
334
|
+
template_name: str = "symbolic_systems",
|
335
|
+
frameworks: list[str] = None,
|
336
|
+
compress: bool = False,
|
337
|
+
chat_model: iModel = None,
|
338
|
+
compress_model: iModel = None,
|
339
|
+
compression_ratio: float = 0.2,
|
340
|
+
compress_kwargs=None,
|
341
|
+
verbose: bool = True,
|
342
|
+
branch: Branch = None,
|
343
|
+
**kwargs,
|
344
|
+
) -> str:
|
345
|
+
"""
|
346
|
+
Optionally compress the input text using perplexity, then build a final
|
347
|
+
"SynthLang" style system prompt that includes frameworks, templates, etc.
|
348
|
+
|
349
|
+
:param text: The original text to be translated/compressed
|
350
|
+
:param template_name: Key in FRAMEWORK_TEMPLATES
|
351
|
+
:param frameworks: e.g. ["math", "optim"] to enable these frameworks
|
352
|
+
:param compress: If True, uses perplexity-based compression
|
353
|
+
:param chat_model: The model for perplexity compression
|
354
|
+
:param compression_ratio: float ratio of final token usage
|
355
|
+
:param compress_kwargs: Additional arguments to pass to compression
|
356
|
+
:return: A string in SynthLang style
|
357
|
+
"""
|
358
|
+
from .synthlang import create_synthlang_system_prompt
|
359
|
+
|
360
|
+
start = timer()
|
361
|
+
chat_model = chat_model or iModel(**Settings.iModel.CHAT)
|
362
|
+
if compress:
|
363
|
+
text = await compress_text(
|
364
|
+
text,
|
365
|
+
chat_model=compress_model or chat_model,
|
366
|
+
target_ratio=compression_ratio,
|
367
|
+
**(compress_kwargs or {}),
|
368
|
+
)
|
369
|
+
|
370
|
+
# Build final system prompt
|
371
|
+
final_prompt = create_synthlang_system_prompt(
|
372
|
+
template_name=template_name,
|
373
|
+
frameworks=frameworks,
|
374
|
+
)
|
375
|
+
|
376
|
+
sys1 = None
|
377
|
+
sys2 = final_prompt
|
378
|
+
if branch and branch.system:
|
379
|
+
sys1 = branch.system
|
380
|
+
branch.msgs.add_message(system=sys2)
|
381
|
+
|
382
|
+
else:
|
383
|
+
branch = Branch(system=final_prompt, chat_model=chat_model)
|
384
|
+
|
385
|
+
from lionagi.service.endpoints.token_calculator import TokenCalculator
|
386
|
+
|
387
|
+
calculator = TokenCalculator()
|
388
|
+
|
389
|
+
len_tokens = calculator.tokenize(text, return_tokens=False)
|
390
|
+
out = await branch.communicate(
|
391
|
+
instruction=f"Converts the given text into SynthLang's hyper-efficient format.",
|
392
|
+
context="Text to convert:\n\n" + text,
|
393
|
+
guidance=f"Following SynthLang, translate the provided text into SynthLang syntax. Shrink the token size by 60-85%. Return only the translated text.",
|
394
|
+
**kwargs,
|
395
|
+
)
|
396
|
+
if sys1:
|
397
|
+
branch.msgs.add_message(system=sys1)
|
398
|
+
|
399
|
+
len_ = calculator.tokenize(out, return_tokens=False)
|
400
|
+
if verbose:
|
401
|
+
msg = ""
|
402
|
+
msg += f"Compression Method: SynthLang\n"
|
403
|
+
msg += f"Compressed Token number: {len_}\n"
|
404
|
+
msg += f"Token Compression Ratio: {len_ / len_tokens:.1%}\n"
|
405
|
+
msg += f"Compression Time: {timer() - start:.03f} seconds\n"
|
406
|
+
msg += f"Compression Model: {branch.chat_model.model_name}\n"
|
407
|
+
print(msg)
|
408
|
+
|
409
|
+
return out
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import TYPE_CHECKING, Any
|
7
|
+
|
8
|
+
from pydantic import BaseModel
|
9
|
+
|
10
|
+
from lionagi.operatives.types import Instruct
|
11
|
+
from lionagi.service.imodel import iModel
|
12
|
+
from lionagi.utils import copy
|
13
|
+
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from lionagi.session.branch import Branch
|
16
|
+
|
17
|
+
|
18
|
+
async def ReAct(
|
19
|
+
branch: "Branch",
|
20
|
+
instruct: Instruct | dict[str, Any],
|
21
|
+
interpret: bool = False,
|
22
|
+
tools: Any = None,
|
23
|
+
tool_schemas: Any = None,
|
24
|
+
response_format: type[BaseModel] = None,
|
25
|
+
extension_allowed: bool = False,
|
26
|
+
max_extensions: int | None = None,
|
27
|
+
response_kwargs: dict | None = None,
|
28
|
+
return_analysis: bool = False,
|
29
|
+
analysis_model: iModel | None = None,
|
30
|
+
**kwargs,
|
31
|
+
):
|
32
|
+
# If no tools or tool schemas are provided, default to "all tools"
|
33
|
+
if not tools and not tool_schemas:
|
34
|
+
tools = True
|
35
|
+
|
36
|
+
# Possibly interpret the instruction to refine it
|
37
|
+
instruction_str = None
|
38
|
+
if interpret:
|
39
|
+
instruction_str = await branch.interpret(
|
40
|
+
str(
|
41
|
+
instruct.to_dict()
|
42
|
+
if isinstance(instruct, Instruct)
|
43
|
+
else instruct
|
44
|
+
)
|
45
|
+
)
|
46
|
+
|
47
|
+
# Convert Instruct to dict if necessary
|
48
|
+
instruct_dict = (
|
49
|
+
instruct.to_dict()
|
50
|
+
if isinstance(instruct, Instruct)
|
51
|
+
else dict(instruct)
|
52
|
+
)
|
53
|
+
# Overwrite the "instruction" field with the interpreted string (if any)
|
54
|
+
instruct_dict["instruction"] = instruction_str or instruct_dict.get(
|
55
|
+
"instruction"
|
56
|
+
)
|
57
|
+
|
58
|
+
# Prepare a copy of user-provided kwargs for the first operate call
|
59
|
+
kwargs_for_operate = copy(kwargs)
|
60
|
+
kwargs_for_operate["actions"] = True
|
61
|
+
kwargs_for_operate["reason"] = True
|
62
|
+
|
63
|
+
# We'll pass the refined instruct_dict plus the user’s other kwargs
|
64
|
+
from .utils import ReActAnalysis
|
65
|
+
|
66
|
+
# Step 1: Generate initial ReAct analysis
|
67
|
+
analysis: ReActAnalysis = await branch.operate(
|
68
|
+
response_format=ReActAnalysis,
|
69
|
+
tools=tools,
|
70
|
+
tool_schemas=tool_schemas,
|
71
|
+
chat_model=analysis_model or branch.chat_model,
|
72
|
+
**kwargs_for_operate,
|
73
|
+
)
|
74
|
+
analyses = [analysis]
|
75
|
+
|
76
|
+
# Validate and clamp max_extensions if needed
|
77
|
+
if max_extensions and max_extensions > 5:
|
78
|
+
logging.warning("max_extensions should not exceed 5; defaulting to 5.")
|
79
|
+
max_extensions = 5
|
80
|
+
|
81
|
+
# Step 2: Possibly loop through expansions if extension_needed
|
82
|
+
extensions = max_extensions
|
83
|
+
while (
|
84
|
+
extension_allowed
|
85
|
+
and analysis.extension_needed
|
86
|
+
and (extensions if extensions else 1) > 0
|
87
|
+
):
|
88
|
+
new_instruction = None
|
89
|
+
if extensions == max_extensions:
|
90
|
+
new_instruction = ReActAnalysis.FIRST_EXT_PROMPT.format(
|
91
|
+
extensions=extensions
|
92
|
+
)
|
93
|
+
else:
|
94
|
+
new_instruction = ReActAnalysis.CONTINUE_EXT_PROMPT.format(
|
95
|
+
extensions=extensions
|
96
|
+
)
|
97
|
+
|
98
|
+
# Each expansion uses a fresh copy of instruct_dict + forcibly "reason" + "actions"
|
99
|
+
expanded_kwargs = copy(instruct_dict)
|
100
|
+
expanded_kwargs["instruction"] = new_instruction
|
101
|
+
expanded_kwargs["reason"] = True
|
102
|
+
expanded_kwargs["actions"] = True
|
103
|
+
|
104
|
+
analysis = await branch.operate(
|
105
|
+
response_format=ReActAnalysis,
|
106
|
+
tools=tools,
|
107
|
+
tool_schemas=tool_schemas,
|
108
|
+
**expanded_kwargs,
|
109
|
+
)
|
110
|
+
analyses.append(analysis)
|
111
|
+
|
112
|
+
if extensions:
|
113
|
+
extensions -= 1
|
114
|
+
|
115
|
+
# Step 3: Produce final answer by calling branch._instruct with an answer prompt
|
116
|
+
answer_prompt = ReActAnalysis.ANSWER_PROMPT.format(
|
117
|
+
instruction=instruct_dict["instruction"]
|
118
|
+
)
|
119
|
+
out = await branch.communicate(
|
120
|
+
instruction=answer_prompt,
|
121
|
+
response_format=response_format,
|
122
|
+
**(response_kwargs or {}),
|
123
|
+
)
|
124
|
+
if return_analysis:
|
125
|
+
return out, analyses
|
126
|
+
return out
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
4
|
+
|
5
|
+
from typing import ClassVar
|
6
|
+
|
7
|
+
from pydantic import BaseModel, Field
|
8
|
+
|
9
|
+
|
10
|
+
class ReActAnalysis(BaseModel):
|
11
|
+
|
12
|
+
FIRST_EXT_PROMPT: ClassVar[str] = (
|
13
|
+
"You are provided with another round to perform reason action to provide an accurate final answer. you have max another {extensions} rounds, set extension_needed to False if you are done and ready to provide final answer."
|
14
|
+
)
|
15
|
+
|
16
|
+
CONTINUE_EXT_PROMPT: ClassVar[str] = (
|
17
|
+
"You are provided with another round, you have max another {extensions} rounds"
|
18
|
+
)
|
19
|
+
|
20
|
+
ANSWER_PROMPT: ClassVar[str] = (
|
21
|
+
"given above reason and actions, please provide final answer to the original user request {instruction}"
|
22
|
+
)
|
23
|
+
|
24
|
+
analysis: str
|
25
|
+
extension_needed: bool = Field(
|
26
|
+
False,
|
27
|
+
description="Set to True if more steps are needed to provide an accurate answer. If True, additional rounds are allowed.",
|
28
|
+
)
|
lionagi/operations/__init__.py
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Copyright (c) 2023 - 2024, HaiyangLi <quantocean.li at gmail dot com>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import TYPE_CHECKING
|
7
|
+
|
8
|
+
from pydantic import BaseModel
|
9
|
+
|
10
|
+
from lionagi.protocols.types import ActionResponse, Log
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from lionagi.session.branch import Branch
|
14
|
+
|
15
|
+
|
16
|
+
async def _act(
|
17
|
+
branch: "Branch",
|
18
|
+
action_request: BaseModel | dict,
|
19
|
+
suppress_errors: bool = False,
|
20
|
+
) -> ActionResponse:
|
21
|
+
|
22
|
+
_request = {}
|
23
|
+
|
24
|
+
if isinstance(action_request, BaseModel):
|
25
|
+
if hasattr(action_request, "function") and hasattr(
|
26
|
+
action_request, "arguments"
|
27
|
+
):
|
28
|
+
_request["function"] = action_request.function
|
29
|
+
_request["arguments"] = action_request.arguments
|
30
|
+
elif isinstance(action_request, dict):
|
31
|
+
if {"function", "arguments"} <= set(action_request.keys()):
|
32
|
+
_request["function"] = action_request["function"]
|
33
|
+
_request["arguments"] = action_request["arguments"]
|
34
|
+
|
35
|
+
try:
|
36
|
+
func_call = await branch._action_manager.invoke(_request)
|
37
|
+
except Exception as e:
|
38
|
+
branch._log_manager.log(Log(content={"error": str(e)}))
|
39
|
+
if suppress_errors:
|
40
|
+
logging.error(
|
41
|
+
f"Error invoking action '{_request['function']}': {e}"
|
42
|
+
)
|
43
|
+
return None
|
44
|
+
raise e
|
45
|
+
|
46
|
+
branch._log_manager.log(Log.create(func_call))
|
47
|
+
|
48
|
+
from lionagi.protocols.types import ActionRequest
|
49
|
+
|
50
|
+
if not isinstance(action_request, ActionRequest):
|
51
|
+
action_request = ActionRequest.create(
|
52
|
+
sender=branch.id,
|
53
|
+
recipient=func_call.func_tool.id,
|
54
|
+
**_request,
|
55
|
+
)
|
56
|
+
|
57
|
+
# Add the action request/response to the message manager, if not present
|
58
|
+
if action_request not in branch.messages:
|
59
|
+
branch.msgs.add_message(action_request=action_request)
|
60
|
+
|
61
|
+
branch.msgs.add_message(
|
62
|
+
action_request=action_request,
|
63
|
+
action_output=func_call.response,
|
64
|
+
)
|
65
|
+
|
66
|
+
# Return an ActionResponse object
|
67
|
+
from lionagi.operatives.types import ActionResponseModel
|
68
|
+
|
69
|
+
return ActionResponseModel(
|
70
|
+
function=action_request.function,
|
71
|
+
arguments=action_request.arguments,
|
72
|
+
output=func_call.response,
|
73
|
+
)
|