jarviscore-framework 0.1.0__py3-none-any.whl → 0.2.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.
- examples/autoagent_distributed_example.py +211 -0
- examples/custom_profile_decorator.py +134 -0
- examples/custom_profile_wrap.py +168 -0
- examples/customagent_distributed_example.py +362 -0
- examples/customagent_p2p_example.py +347 -0
- jarviscore/__init__.py +60 -15
- jarviscore/adapter/__init__.py +40 -0
- jarviscore/adapter/decorator.py +336 -0
- jarviscore/adapter/wrapper.py +303 -0
- jarviscore/cli/check.py +18 -13
- jarviscore/cli/scaffold.py +178 -0
- jarviscore/cli/smoketest.py +3 -2
- jarviscore/context/__init__.py +40 -0
- jarviscore/context/dependency.py +160 -0
- jarviscore/context/jarvis_context.py +207 -0
- jarviscore/context/memory.py +155 -0
- jarviscore/core/agent.py +44 -1
- jarviscore/core/mesh.py +196 -35
- jarviscore/data/.env.example +146 -0
- jarviscore/data/__init__.py +7 -0
- jarviscore/data/examples/autoagent_distributed_example.py +211 -0
- jarviscore/data/examples/calculator_agent_example.py +77 -0
- jarviscore/data/examples/customagent_distributed_example.py +362 -0
- jarviscore/data/examples/customagent_p2p_example.py +347 -0
- jarviscore/data/examples/multi_agent_workflow.py +132 -0
- jarviscore/data/examples/research_agent_example.py +76 -0
- jarviscore/docs/API_REFERENCE.md +264 -51
- jarviscore/docs/AUTOAGENT_GUIDE.md +198 -0
- jarviscore/docs/CONFIGURATION.md +41 -23
- jarviscore/docs/CUSTOMAGENT_GUIDE.md +415 -0
- jarviscore/docs/GETTING_STARTED.md +113 -17
- jarviscore/docs/TROUBLESHOOTING.md +155 -13
- jarviscore/docs/USER_GUIDE.md +144 -363
- jarviscore/execution/llm.py +23 -16
- jarviscore/orchestration/engine.py +20 -8
- jarviscore/p2p/__init__.py +10 -0
- jarviscore/p2p/coordinator.py +129 -0
- jarviscore/p2p/messages.py +87 -0
- jarviscore/p2p/peer_client.py +576 -0
- jarviscore/p2p/peer_tool.py +268 -0
- jarviscore_framework-0.2.0.dist-info/METADATA +143 -0
- jarviscore_framework-0.2.0.dist-info/RECORD +132 -0
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/WHEEL +1 -1
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/top_level.txt +1 -0
- test_logs/code_registry/functions/data_generator-558779ed_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-5ed3609e_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-66da0356_43970bb9.py +25 -0
- test_logs/code_registry/functions/data_generator-7a2fac83_583709d9.py +36 -0
- test_logs/code_registry/functions/data_generator-888b670f_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-9ca5f642_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-bfd90775_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-e95d2f7d_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-f60ca8a2_327eb8c2.py +29 -0
- test_logs/code_registry/functions/mathematician-02adf9ee_958658d9.py +19 -0
- test_logs/code_registry/functions/mathematician-0706fb57_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-153c9c4a_ba59c918.py +83 -0
- test_logs/code_registry/functions/mathematician-287e61c0_41daa793.py +18 -0
- test_logs/code_registry/functions/mathematician-2967af5a_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-303ca6d6_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-308a4afd_cbf5064d.py +73 -0
- test_logs/code_registry/functions/mathematician-353f16e2_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-3c22475a_41daa793.py +17 -0
- test_logs/code_registry/functions/mathematician-5bac1029_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-640f76b2_9198780b.py +19 -0
- test_logs/code_registry/functions/mathematician-752fa7ea_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-baf9ef39_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-bc8b2a2f_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-c31e4686_41daa793.py +18 -0
- test_logs/code_registry/functions/mathematician-cc84c84c_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-dd7c7144_9198780b.py +19 -0
- test_logs/code_registry/functions/mathematician-e671c256_41ea4487.py +74 -0
- test_logs/code_registry/functions/report_generator-1a878fcc_18d44bdc.py +47 -0
- test_logs/code_registry/functions/report_generator-25c1c331_cea57d0d.py +35 -0
- test_logs/code_registry/functions/report_generator-37552117_e711c2b9.py +35 -0
- test_logs/code_registry/functions/report_generator-bc662768_e711c2b9.py +35 -0
- test_logs/code_registry/functions/report_generator-d6c0e76b_5e7722ec.py +44 -0
- test_logs/code_registry/functions/report_generator-f270fb02_680529c3.py +44 -0
- test_logs/code_registry/functions/text_processor-11393b14_4370d3ed.py +40 -0
- test_logs/code_registry/functions/text_processor-7d02dfc3_d3b569be.py +37 -0
- test_logs/code_registry/functions/text_processor-8adb5e32_9168c5fe.py +13 -0
- test_logs/code_registry/functions/text_processor-c58ffc19_78b4ceac.py +42 -0
- test_logs/code_registry/functions/text_processor-cd5977b1_9168c5fe.py +13 -0
- test_logs/code_registry/functions/text_processor-ec1c8773_9168c5fe.py +13 -0
- tests/test_01_analyst_standalone.py +124 -0
- tests/test_02_assistant_standalone.py +164 -0
- tests/test_03_analyst_with_framework.py +945 -0
- tests/test_04_assistant_with_framework.py +1002 -0
- tests/test_05_integration.py +1301 -0
- tests/test_06_real_llm_integration.py +760 -0
- tests/test_07_distributed_single_node.py +578 -0
- tests/test_08_distributed_multi_node.py +454 -0
- tests/test_09_distributed_autoagent.py +509 -0
- tests/test_10_distributed_customagent.py +787 -0
- tests/test_context.py +467 -0
- tests/test_decorator.py +622 -0
- tests/test_mesh.py +35 -4
- jarviscore_framework-0.1.0.dist-info/METADATA +0 -136
- jarviscore_framework-0.1.0.dist-info/RECORD +0 -55
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
try:
|
|
3
|
+
# Create invoice header with company name and date
|
|
4
|
+
company_name = "Acme Corp"
|
|
5
|
+
date = "Jan 2024"
|
|
6
|
+
|
|
7
|
+
# Create formatted header
|
|
8
|
+
width = 50
|
|
9
|
+
border = "=" * width
|
|
10
|
+
|
|
11
|
+
header_lines = [
|
|
12
|
+
border,
|
|
13
|
+
"",
|
|
14
|
+
company_name.center(width),
|
|
15
|
+
"",
|
|
16
|
+
"INVOICE".center(width),
|
|
17
|
+
"",
|
|
18
|
+
f"Date: {date}".center(width),
|
|
19
|
+
"",
|
|
20
|
+
border
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
report = "\n".join(header_lines)
|
|
24
|
+
|
|
25
|
+
result = {
|
|
26
|
+
'report': report
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
except Exception as e:
|
|
32
|
+
result = {
|
|
33
|
+
'report': f"Error generating invoice header: {str(e)}"
|
|
34
|
+
}
|
|
35
|
+
return result
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
try:
|
|
3
|
+
# Create invoice header with company name and date
|
|
4
|
+
company_name = "Acme Corp"
|
|
5
|
+
date = "Jan 2024"
|
|
6
|
+
|
|
7
|
+
# Create formatted header
|
|
8
|
+
width = 50
|
|
9
|
+
border = "=" * width
|
|
10
|
+
|
|
11
|
+
header_lines = [
|
|
12
|
+
border,
|
|
13
|
+
"",
|
|
14
|
+
company_name.center(width),
|
|
15
|
+
"",
|
|
16
|
+
"INVOICE".center(width),
|
|
17
|
+
"",
|
|
18
|
+
f"Date: {date}".center(width),
|
|
19
|
+
"",
|
|
20
|
+
border
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
report = "\n".join(header_lines)
|
|
24
|
+
|
|
25
|
+
result = {
|
|
26
|
+
'report': report
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
except Exception as e:
|
|
32
|
+
result = {
|
|
33
|
+
'report': f"Error generating invoice header: {str(e)}"
|
|
34
|
+
}
|
|
35
|
+
return result
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
try:
|
|
3
|
+
# Get data from previous step if available, otherwise use provided data
|
|
4
|
+
previous_data = {'name': 'John', 'age': 30, 'city': 'NYC'}
|
|
5
|
+
|
|
6
|
+
# Check if we have previous step results
|
|
7
|
+
try:
|
|
8
|
+
if 'step0' in previous_step_results:
|
|
9
|
+
previous_data = previous_step_results['step0']
|
|
10
|
+
except NameError:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
name = previous_data.get('name', 'John')
|
|
14
|
+
age = previous_data.get('age', 30)
|
|
15
|
+
city = previous_data.get('city', 'NYC')
|
|
16
|
+
|
|
17
|
+
# Create formatted text report
|
|
18
|
+
report_lines = [
|
|
19
|
+
"=" * 40,
|
|
20
|
+
"PERSON INFORMATION REPORT",
|
|
21
|
+
"=" * 40,
|
|
22
|
+
"",
|
|
23
|
+
f"{'Name:':<15} {name}",
|
|
24
|
+
f"{'Age:':<15} {age}",
|
|
25
|
+
f"{'City:':<15} {city}",
|
|
26
|
+
"",
|
|
27
|
+
"-" * 40,
|
|
28
|
+
f"Report generated successfully",
|
|
29
|
+
"=" * 40
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
report = "\n".join(report_lines)
|
|
33
|
+
|
|
34
|
+
result = {
|
|
35
|
+
'report': report
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
except Exception as e:
|
|
41
|
+
result = {
|
|
42
|
+
'report': f"Error generating report: {str(e)}"
|
|
43
|
+
}
|
|
44
|
+
return result
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
try:
|
|
3
|
+
# Get data from previous step if available, otherwise use provided defaults
|
|
4
|
+
previous_data = {'name': 'John', 'age': 30, 'city': 'NYC'}
|
|
5
|
+
|
|
6
|
+
# Check if we have previous step results
|
|
7
|
+
try:
|
|
8
|
+
if 'step0' in globals().get('previous_step_results', {}):
|
|
9
|
+
previous_data = previous_step_results['step0']
|
|
10
|
+
except:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
# Extract person data
|
|
14
|
+
name = previous_data.get('name', 'John')
|
|
15
|
+
age = previous_data.get('age', 30)
|
|
16
|
+
city = previous_data.get('city', 'NYC')
|
|
17
|
+
|
|
18
|
+
# Create formatted text report
|
|
19
|
+
report_lines = [
|
|
20
|
+
"=" * 40,
|
|
21
|
+
"PERSON INFORMATION REPORT",
|
|
22
|
+
"=" * 40,
|
|
23
|
+
"",
|
|
24
|
+
f"Name: {name}",
|
|
25
|
+
f"Age: {age}",
|
|
26
|
+
f"City: {city}",
|
|
27
|
+
"",
|
|
28
|
+
"-" * 40,
|
|
29
|
+
f"Generated for: {name}",
|
|
30
|
+
"=" * 40
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
report = "\n".join(report_lines)
|
|
34
|
+
|
|
35
|
+
result = {
|
|
36
|
+
'report': report
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
except Exception as e:
|
|
42
|
+
return {
|
|
43
|
+
'report': f"Error generating report: {str(e)}"
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
def main():
|
|
2
|
+
# Input product list
|
|
3
|
+
product_string = "Widget=$10, Gadget=$25, Tool=$15"
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
# Split the string by comma and strip whitespace
|
|
7
|
+
products = [item.strip() for item in product_string.split(',')]
|
|
8
|
+
|
|
9
|
+
# Create bulleted list
|
|
10
|
+
bulleted_items = []
|
|
11
|
+
for product in products:
|
|
12
|
+
# Split by '=' to separate name and price
|
|
13
|
+
parts = product.split('=')
|
|
14
|
+
if len(parts) == 2:
|
|
15
|
+
name = parts[0].strip()
|
|
16
|
+
price = parts[1].strip()
|
|
17
|
+
bulleted_items.append(f"• {name}: {price}")
|
|
18
|
+
else:
|
|
19
|
+
bulleted_items.append(f"• {product}")
|
|
20
|
+
|
|
21
|
+
# Join into final bulleted text
|
|
22
|
+
bulleted_text = '\n'.join(bulleted_items)
|
|
23
|
+
|
|
24
|
+
result = {
|
|
25
|
+
'original': product_string,
|
|
26
|
+
'bulleted_list': bulleted_text,
|
|
27
|
+
'item_count': len(bulleted_items),
|
|
28
|
+
'items': bulleted_items
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return {
|
|
35
|
+
'error': str(e),
|
|
36
|
+
'original': product_string,
|
|
37
|
+
'bulleted_list': None
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
result = main()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
try:
|
|
3
|
+
# Input product list
|
|
4
|
+
product_string = "Widget=$10, Gadget=$25, Tool=$15"
|
|
5
|
+
|
|
6
|
+
# Parse the product list
|
|
7
|
+
products = product_string.split(", ")
|
|
8
|
+
|
|
9
|
+
# Format as bulleted list
|
|
10
|
+
bulleted_lines = []
|
|
11
|
+
for product in products:
|
|
12
|
+
# Split by = to get name and price
|
|
13
|
+
parts = product.split("=")
|
|
14
|
+
if len(parts) == 2:
|
|
15
|
+
name = parts[0].strip()
|
|
16
|
+
price = parts[1].strip()
|
|
17
|
+
bulleted_lines.append(f"• {name}: {price}")
|
|
18
|
+
|
|
19
|
+
# Join into final bulleted text
|
|
20
|
+
bulleted_text = "\n".join(bulleted_lines)
|
|
21
|
+
|
|
22
|
+
result = {
|
|
23
|
+
"original": product_string,
|
|
24
|
+
"bulleted_list": bulleted_text,
|
|
25
|
+
"item_count": len(bulleted_lines),
|
|
26
|
+
"items": [{"name": p.split("=")[0].strip(), "price": p.split("=")[1].strip()} for p in products]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
except Exception as e:
|
|
32
|
+
result = {
|
|
33
|
+
"error": str(e),
|
|
34
|
+
"bulleted_list": "",
|
|
35
|
+
"item_count": 0
|
|
36
|
+
}
|
|
37
|
+
return result
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
words = ["apple", "banana", "cherry", "date", "elderberry"]
|
|
3
|
+
|
|
4
|
+
word_lengths = {word: len(word) for word in words}
|
|
5
|
+
total_characters = sum(word_lengths.values())
|
|
6
|
+
|
|
7
|
+
result = {
|
|
8
|
+
"words": words,
|
|
9
|
+
"individual_lengths": word_lengths,
|
|
10
|
+
"total_characters": total_characters
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return result
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
def main():
|
|
2
|
+
# Input product list
|
|
3
|
+
product_string = "Widget=$10, Gadget=$25, Tool=$15"
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
# Split the string by comma and strip whitespace
|
|
7
|
+
products = [item.strip() for item in product_string.split(",")]
|
|
8
|
+
|
|
9
|
+
# Create bulleted list
|
|
10
|
+
bulleted_items = []
|
|
11
|
+
for product in products:
|
|
12
|
+
# Split by = to get name and price
|
|
13
|
+
parts = product.split("=")
|
|
14
|
+
if len(parts) == 2:
|
|
15
|
+
name = parts[0].strip()
|
|
16
|
+
price = parts[1].strip()
|
|
17
|
+
bulleted_items.append(f"• {name}: {price}")
|
|
18
|
+
else:
|
|
19
|
+
bulleted_items.append(f"• {product}")
|
|
20
|
+
|
|
21
|
+
# Join into final bulleted text
|
|
22
|
+
bulleted_text = "\n".join(bulleted_items)
|
|
23
|
+
|
|
24
|
+
result = {
|
|
25
|
+
"original": product_string,
|
|
26
|
+
"bulleted_list": bulleted_text,
|
|
27
|
+
"item_count": len(bulleted_items),
|
|
28
|
+
"items": bulleted_items
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return {
|
|
35
|
+
"error": str(e),
|
|
36
|
+
"original": product_string,
|
|
37
|
+
"bulleted_list": "",
|
|
38
|
+
"item_count": 0,
|
|
39
|
+
"items": []
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
result = main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
words = ["apple", "banana", "cherry", "date", "elderberry"]
|
|
3
|
+
|
|
4
|
+
word_lengths = {word: len(word) for word in words}
|
|
5
|
+
total_characters = sum(word_lengths.values())
|
|
6
|
+
|
|
7
|
+
result = {
|
|
8
|
+
"words": words,
|
|
9
|
+
"individual_lengths": word_lengths,
|
|
10
|
+
"total_characters": total_characters
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return result
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async def main():
|
|
2
|
+
words = ["apple", "banana", "cherry", "date", "elderberry"]
|
|
3
|
+
|
|
4
|
+
word_lengths = {word: len(word) for word in words}
|
|
5
|
+
total_characters = sum(word_lengths.values())
|
|
6
|
+
|
|
7
|
+
result = {
|
|
8
|
+
"words": words,
|
|
9
|
+
"individual_lengths": word_lengths,
|
|
10
|
+
"total_characters": total_characters
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return result
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test 1: Standalone Analyst
|
|
3
|
+
|
|
4
|
+
The Analyst agent working alone - no framework.
|
|
5
|
+
Can analyze data, but cannot receive requests from other agents.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Analyst:
|
|
10
|
+
"""
|
|
11
|
+
Standalone Analyst agent.
|
|
12
|
+
|
|
13
|
+
Capabilities:
|
|
14
|
+
- Analyze data
|
|
15
|
+
- Generate reports
|
|
16
|
+
- Provide recommendations
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self.name = "analyst"
|
|
21
|
+
self.analyses_count = 0
|
|
22
|
+
|
|
23
|
+
def analyze(self, data: str) -> dict:
|
|
24
|
+
"""Analyze data and return insights."""
|
|
25
|
+
self.analyses_count += 1
|
|
26
|
+
return {
|
|
27
|
+
"id": self.analyses_count,
|
|
28
|
+
"input": data,
|
|
29
|
+
"summary": f"Analysis of '{data}' shows positive trends",
|
|
30
|
+
"confidence": 0.85,
|
|
31
|
+
"recommendation": "Proceed with caution"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def generate_report(self, analysis: dict) -> str:
|
|
35
|
+
"""Generate a text report from analysis."""
|
|
36
|
+
return (
|
|
37
|
+
f"Report #{analysis['id']}\n"
|
|
38
|
+
f"Summary: {analysis['summary']}\n"
|
|
39
|
+
f"Confidence: {analysis['confidence']}\n"
|
|
40
|
+
f"Recommendation: {analysis['recommendation']}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
45
|
+
# TESTS
|
|
46
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
47
|
+
|
|
48
|
+
def test_analyst_init():
|
|
49
|
+
"""Analyst initializes correctly."""
|
|
50
|
+
analyst = Analyst()
|
|
51
|
+
assert analyst.name == "analyst"
|
|
52
|
+
assert analyst.analyses_count == 0
|
|
53
|
+
print("✓ Analyst initialized")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_analyst_analyze():
|
|
57
|
+
"""Analyst can analyze data."""
|
|
58
|
+
analyst = Analyst()
|
|
59
|
+
result = analyst.analyze("Q4 sales data")
|
|
60
|
+
|
|
61
|
+
assert result["id"] == 1
|
|
62
|
+
assert result["confidence"] == 0.85
|
|
63
|
+
assert "Q4 sales data" in result["summary"]
|
|
64
|
+
print(f"✓ Analysis: {result['summary']}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_analyst_multiple_analyses():
|
|
68
|
+
"""Analyst tracks analysis count."""
|
|
69
|
+
analyst = Analyst()
|
|
70
|
+
|
|
71
|
+
analyst.analyze("data 1")
|
|
72
|
+
analyst.analyze("data 2")
|
|
73
|
+
result = analyst.analyze("data 3")
|
|
74
|
+
|
|
75
|
+
assert analyst.analyses_count == 3
|
|
76
|
+
assert result["id"] == 3
|
|
77
|
+
print(f"✓ Multiple analyses: count = {analyst.analyses_count}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_analyst_generate_report():
|
|
81
|
+
"""Analyst can generate reports."""
|
|
82
|
+
analyst = Analyst()
|
|
83
|
+
analysis = analyst.analyze("market trends")
|
|
84
|
+
report = analyst.generate_report(analysis)
|
|
85
|
+
|
|
86
|
+
assert "Report #1" in report
|
|
87
|
+
assert "market trends" in report
|
|
88
|
+
print(f"✓ Report generated:\n{report}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_analyst_cannot_receive_requests():
|
|
92
|
+
"""Analyst has NO way to receive external requests."""
|
|
93
|
+
analyst = Analyst()
|
|
94
|
+
|
|
95
|
+
# No peers
|
|
96
|
+
assert not hasattr(analyst, 'peers')
|
|
97
|
+
|
|
98
|
+
# No receive method
|
|
99
|
+
assert not hasattr(analyst, 'receive')
|
|
100
|
+
|
|
101
|
+
# No way to listen for incoming messages
|
|
102
|
+
assert not hasattr(analyst, 'run')
|
|
103
|
+
|
|
104
|
+
print("✓ Analyst CANNOT receive external requests (limitation)")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
108
|
+
# RUN
|
|
109
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
print("\n" + "="*60)
|
|
113
|
+
print("TEST 1: STANDALONE ANALYST")
|
|
114
|
+
print("="*60 + "\n")
|
|
115
|
+
|
|
116
|
+
test_analyst_init()
|
|
117
|
+
test_analyst_analyze()
|
|
118
|
+
test_analyst_multiple_analyses()
|
|
119
|
+
test_analyst_generate_report()
|
|
120
|
+
test_analyst_cannot_receive_requests()
|
|
121
|
+
|
|
122
|
+
print("\n" + "-"*60)
|
|
123
|
+
print("Analyst works, but cannot receive requests from others.")
|
|
124
|
+
print("-"*60 + "\n")
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test 2: Standalone Assistant
|
|
3
|
+
|
|
4
|
+
The Assistant agent working alone - no framework.
|
|
5
|
+
Has local tools (search, calculate) but cannot talk to other agents.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Assistant:
|
|
10
|
+
"""
|
|
11
|
+
Standalone Assistant agent.
|
|
12
|
+
|
|
13
|
+
Capabilities:
|
|
14
|
+
- Search the web
|
|
15
|
+
- Calculate expressions
|
|
16
|
+
- Chat with user
|
|
17
|
+
|
|
18
|
+
Tools available to LLM:
|
|
19
|
+
- search
|
|
20
|
+
- calculate
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
self.name = "assistant"
|
|
25
|
+
|
|
26
|
+
def search(self, query: str) -> str:
|
|
27
|
+
"""Search the web for information."""
|
|
28
|
+
# Simulated search
|
|
29
|
+
return f"Search results for '{query}': Found 10 relevant articles."
|
|
30
|
+
|
|
31
|
+
def calculate(self, expression: str) -> str:
|
|
32
|
+
"""Calculate a math expression."""
|
|
33
|
+
try:
|
|
34
|
+
result = eval(expression)
|
|
35
|
+
return f"Result: {result}"
|
|
36
|
+
except Exception as e:
|
|
37
|
+
return f"Error: {e}"
|
|
38
|
+
|
|
39
|
+
def get_tools(self) -> list:
|
|
40
|
+
"""Return tool definitions for LLM."""
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
"name": "search",
|
|
44
|
+
"description": "Search the web for information",
|
|
45
|
+
"parameters": {"query": "string"}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "calculate",
|
|
49
|
+
"description": "Calculate a math expression",
|
|
50
|
+
"parameters": {"expression": "string"}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
def execute_tool(self, tool_name: str, args: dict) -> str:
|
|
55
|
+
"""Execute a tool by name."""
|
|
56
|
+
if tool_name == "search":
|
|
57
|
+
return self.search(args.get("query", ""))
|
|
58
|
+
elif tool_name == "calculate":
|
|
59
|
+
return self.calculate(args.get("expression", ""))
|
|
60
|
+
else:
|
|
61
|
+
return f"Unknown tool: {tool_name}"
|
|
62
|
+
|
|
63
|
+
def chat(self, message: str) -> str:
|
|
64
|
+
"""Simple chat response."""
|
|
65
|
+
return f"I received: {message}"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
69
|
+
# TESTS
|
|
70
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
def test_assistant_init():
|
|
73
|
+
"""Assistant initializes correctly."""
|
|
74
|
+
assistant = Assistant()
|
|
75
|
+
assert assistant.name == "assistant"
|
|
76
|
+
print("✓ Assistant initialized")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_assistant_search():
|
|
80
|
+
"""Assistant can search."""
|
|
81
|
+
assistant = Assistant()
|
|
82
|
+
result = assistant.search("market trends 2024")
|
|
83
|
+
|
|
84
|
+
assert "Search results" in result
|
|
85
|
+
assert "market trends" in result
|
|
86
|
+
print(f"✓ Search: {result}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_assistant_calculate():
|
|
90
|
+
"""Assistant can calculate."""
|
|
91
|
+
assistant = Assistant()
|
|
92
|
+
|
|
93
|
+
result1 = assistant.calculate("2 + 2")
|
|
94
|
+
assert "4" in result1
|
|
95
|
+
print(f"✓ Calculate 2+2: {result1}")
|
|
96
|
+
|
|
97
|
+
result2 = assistant.calculate("100 * 0.15")
|
|
98
|
+
assert "15" in result2
|
|
99
|
+
print(f"✓ Calculate 100*0.15: {result2}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_assistant_get_tools():
|
|
103
|
+
"""Assistant returns tool definitions."""
|
|
104
|
+
assistant = Assistant()
|
|
105
|
+
tools = assistant.get_tools()
|
|
106
|
+
tool_names = [t["name"] for t in tools]
|
|
107
|
+
|
|
108
|
+
assert "search" in tool_names
|
|
109
|
+
assert "calculate" in tool_names
|
|
110
|
+
assert len(tools) == 2
|
|
111
|
+
print(f"✓ Tools: {tool_names}")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_assistant_execute_tool():
|
|
115
|
+
"""Assistant can execute tools by name."""
|
|
116
|
+
assistant = Assistant()
|
|
117
|
+
|
|
118
|
+
result = assistant.execute_tool("search", {"query": "python tutorials"})
|
|
119
|
+
assert "python tutorials" in result
|
|
120
|
+
print(f"✓ Execute search: {result}")
|
|
121
|
+
|
|
122
|
+
result = assistant.execute_tool("calculate", {"expression": "10 / 2"})
|
|
123
|
+
assert "5" in result
|
|
124
|
+
print(f"✓ Execute calculate: {result}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_assistant_cannot_talk_to_peers():
|
|
128
|
+
"""Assistant has NO way to talk to other agents."""
|
|
129
|
+
assistant = Assistant()
|
|
130
|
+
tools = assistant.get_tools()
|
|
131
|
+
tool_names = [t["name"] for t in tools]
|
|
132
|
+
|
|
133
|
+
# No peer communication tools
|
|
134
|
+
assert "ask_peer" not in tool_names
|
|
135
|
+
assert "broadcast_update" not in tool_names
|
|
136
|
+
assert "list_peers" not in tool_names
|
|
137
|
+
|
|
138
|
+
# No peers attribute
|
|
139
|
+
assert not hasattr(assistant, 'peers')
|
|
140
|
+
|
|
141
|
+
print("✓ Assistant CANNOT talk to peers (limitation)")
|
|
142
|
+
print(f" Available tools: {tool_names}")
|
|
143
|
+
print(f" Missing: ask_peer, broadcast_update, list_peers")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
147
|
+
# RUN
|
|
148
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
149
|
+
|
|
150
|
+
if __name__ == "__main__":
|
|
151
|
+
print("\n" + "="*60)
|
|
152
|
+
print("TEST 2: STANDALONE ASSISTANT")
|
|
153
|
+
print("="*60 + "\n")
|
|
154
|
+
|
|
155
|
+
test_assistant_init()
|
|
156
|
+
test_assistant_search()
|
|
157
|
+
test_assistant_calculate()
|
|
158
|
+
test_assistant_get_tools()
|
|
159
|
+
test_assistant_execute_tool()
|
|
160
|
+
test_assistant_cannot_talk_to_peers()
|
|
161
|
+
|
|
162
|
+
print("\n" + "-"*60)
|
|
163
|
+
print("Assistant works, but cannot talk to other agents.")
|
|
164
|
+
print("-"*60 + "\n")
|