lionagi 0.6.1__py3-none-any.whl → 0.7.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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/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 +16 -0
- lionagi/operatives/action/manager.py +20 -21
- lionagi/operatives/strategies/__init__.py +3 -0
- lionagi/session/branch.py +1098 -929
- lionagi/version.py +1 -1
- {lionagi-0.6.1.dist-info → lionagi-0.7.0.dist-info}/METADATA +1 -1
- {lionagi-0.6.1.dist-info → lionagi-0.7.0.dist-info}/RECORD +45 -26
- lionagi/libs/compress/models.py +0 -66
- lionagi/libs/compress/utils.py +0 -69
- lionagi/operations/select/prompt.py +0 -5
- /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/params.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.1.dist-info → lionagi-0.7.0.dist-info}/WHEEL +0 -0
- {lionagi-0.6.1.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
|
+
)
|