connectonion 0.4.11__py3-none-any.whl → 0.5.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.
- connectonion/__init__.py +11 -5
- connectonion/agent.py +44 -42
- connectonion/cli/commands/init.py +1 -1
- connectonion/cli/commands/project_cmd_lib.py +4 -4
- connectonion/cli/commands/reset_commands.py +1 -1
- connectonion/cli/docs/co-vibecoding-principles-docs-contexts-all-in-one.md +15 -11
- connectonion/cli/templates/minimal/agent.py +2 -2
- connectonion/console.py +55 -3
- connectonion/events.py +96 -17
- connectonion/llm.py +21 -3
- connectonion/logger.py +289 -0
- connectonion/prompt_files/eval_expected.md +12 -0
- connectonion/tool_executor.py +43 -32
- connectonion/usage.py +4 -0
- connectonion/useful_events_handlers/reflect.py +14 -10
- connectonion/useful_plugins/__init__.py +2 -1
- connectonion/useful_plugins/calendar_plugin.py +2 -2
- connectonion/useful_plugins/eval.py +130 -0
- connectonion/useful_plugins/gmail_plugin.py +4 -4
- connectonion/useful_plugins/image_result_formatter.py +4 -3
- connectonion/useful_plugins/re_act.py +15 -57
- connectonion/useful_plugins/shell_approval.py +2 -2
- connectonion/useful_tools/gmail.py +2 -2
- connectonion/useful_tools/memory.py +4 -0
- {connectonion-0.4.11.dist-info → connectonion-0.5.0.dist-info}/METADATA +48 -48
- {connectonion-0.4.11.dist-info → connectonion-0.5.0.dist-info}/RECORD +33 -77
- {connectonion-0.4.11.dist-info → connectonion-0.5.0.dist-info}/WHEEL +1 -2
- connectonion/cli/templates/email-agent/.env.example +0 -23
- connectonion/cli/templates/email-agent/README.md +0 -240
- connectonion/cli/templates/email-agent/agent.py +0 -374
- connectonion/cli/templates/email-agent/demo.py +0 -71
- connectonion/cli/templates/meta-agent/.env.example +0 -11
- connectonion/cli/templates/minimal/.env.example +0 -5
- connectonion/cli/templates/playwright/.env.example +0 -5
- connectonion-0.4.11.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/cli/__init__.py +0 -1
- tests/cli/argparse_runner.py +0 -85
- tests/cli/conftest.py +0 -5
- tests/cli/test_browser_cli.py +0 -61
- tests/cli/test_cli.py +0 -143
- tests/cli/test_cli_auth_google.py +0 -344
- tests/cli/test_cli_auth_microsoft.py +0 -256
- tests/cli/test_cli_create.py +0 -283
- tests/cli/test_cli_help.py +0 -200
- tests/cli/test_cli_init.py +0 -318
- tests/conftest.py +0 -283
- tests/debug_gemini_models.py +0 -23
- tests/fixtures/__init__.py +0 -1
- tests/fixtures/test_tools.py +0 -112
- tests/fixtures/trust_fixtures.py +0 -257
- tests/real_api/__init__.py +0 -0
- tests/real_api/conftest.py +0 -9
- tests/real_api/test_llm_do.py +0 -174
- tests/real_api/test_llm_do_comprehensive.py +0 -527
- tests/real_api/test_production_client.py +0 -94
- tests/real_api/test_real_anthropic.py +0 -100
- tests/real_api/test_real_api.py +0 -113
- tests/real_api/test_real_auth.py +0 -130
- tests/real_api/test_real_email.py +0 -95
- tests/real_api/test_real_gemini.py +0 -96
- tests/real_api/test_real_llm_do.py +0 -81
- tests/real_api/test_real_managed.py +0 -208
- tests/real_api/test_real_multi_llm.py +0 -454
- tests/real_api/test_real_openai.py +0 -100
- tests/real_api/test_responses_parse.py +0 -88
- tests/test_diff_writer.py +0 -126
- tests/test_events.py +0 -677
- tests/test_gemini_co.py +0 -70
- tests/test_image_result_formatter.py +0 -88
- tests/test_plugin_system.py +0 -110
- tests/utils/__init__.py +0 -1
- tests/utils/config_helpers.py +0 -188
- tests/utils/mock_helpers.py +0 -237
- /connectonion/{prompts → prompt_files}/__init__.py +0 -0
- /connectonion/{prompts → prompt_files}/analyze_contact.md +0 -0
- /connectonion/{prompts → prompt_files}/react_evaluate.md +0 -0
- /connectonion/{prompts → prompt_files}/react_plan.md +0 -0
- /connectonion/{prompts → prompt_files}/reflect.md +0 -0
- {connectonion-0.4.11.dist-info → connectonion-0.5.0.dist-info}/entry_points.txt +0 -0
tests/conftest.py
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
"""Pytest configuration and shared fixtures for ConnectOnion tests."""
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
import tempfile
|
|
5
|
-
import shutil
|
|
6
|
-
import json
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from unittest.mock import Mock, MagicMock
|
|
9
|
-
from connectonion import Agent
|
|
10
|
-
# No need to import tools - they're just functions
|
|
11
|
-
from connectonion.llm import LLMResponse, ToolCall, OpenAILLM
|
|
12
|
-
from connectonion.usage import TokenUsage
|
|
13
|
-
import os
|
|
14
|
-
from dotenv import load_dotenv
|
|
15
|
-
|
|
16
|
-
# Load .env file for API keys
|
|
17
|
-
load_dotenv()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@pytest.fixture
|
|
21
|
-
def temp_dir():
|
|
22
|
-
"""Create a temporary directory for tests."""
|
|
23
|
-
temp_dir = tempfile.mkdtemp()
|
|
24
|
-
yield temp_dir
|
|
25
|
-
shutil.rmtree(temp_dir)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@pytest.fixture
|
|
29
|
-
def mock_openai_client():
|
|
30
|
-
"""Create a mock OpenAI client for testing."""
|
|
31
|
-
mock_client = MagicMock()
|
|
32
|
-
|
|
33
|
-
# Default successful response
|
|
34
|
-
mock_response = MagicMock()
|
|
35
|
-
mock_response.choices = [MagicMock()]
|
|
36
|
-
mock_response.choices[0].message.content = "Test response"
|
|
37
|
-
mock_response.choices[0].message.tool_calls = None
|
|
38
|
-
|
|
39
|
-
mock_client.chat.completions.create.return_value = mock_response
|
|
40
|
-
return mock_client
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@pytest.fixture
|
|
44
|
-
def mock_llm():
|
|
45
|
-
"""Create a mock LLM instance."""
|
|
46
|
-
mock = Mock(spec=OpenAILLM)
|
|
47
|
-
mock.complete.return_value = LLMResponse(
|
|
48
|
-
content="Mock response",
|
|
49
|
-
tool_calls=[],
|
|
50
|
-
raw_response=None,
|
|
51
|
-
usage=TokenUsage(),
|
|
52
|
-
)
|
|
53
|
-
return mock
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# Tool functions for testing
|
|
57
|
-
def calculator(expression: str) -> str:
|
|
58
|
-
"""Perform mathematical calculations."""
|
|
59
|
-
try:
|
|
60
|
-
result = eval(expression)
|
|
61
|
-
return f"Result: {result}"
|
|
62
|
-
except Exception as e:
|
|
63
|
-
return f"Error: {str(e)}"
|
|
64
|
-
|
|
65
|
-
def current_time() -> str:
|
|
66
|
-
"""Get the current time."""
|
|
67
|
-
from datetime import datetime
|
|
68
|
-
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
69
|
-
|
|
70
|
-
def read_file(file_path: str) -> str:
|
|
71
|
-
"""Read content from a file."""
|
|
72
|
-
try:
|
|
73
|
-
with open(file_path, 'r') as f:
|
|
74
|
-
return f.read()
|
|
75
|
-
except FileNotFoundError:
|
|
76
|
-
return f"Error: File not found: {file_path}"
|
|
77
|
-
except Exception as e:
|
|
78
|
-
return f"Error: {str(e)}"
|
|
79
|
-
|
|
80
|
-
@pytest.fixture
|
|
81
|
-
def sample_tools():
|
|
82
|
-
"""Standard set of test tools."""
|
|
83
|
-
return [calculator, current_time, read_file]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
@pytest.fixture
|
|
87
|
-
def test_agent(temp_dir, mock_llm, sample_tools):
|
|
88
|
-
"""Create a test agent with logging to temp directory."""
|
|
89
|
-
# Use temp directory for logging instead of default .co/logs/
|
|
90
|
-
log_file = Path(temp_dir) / "test_agent.log"
|
|
91
|
-
agent = Agent(name="test_agent", llm=mock_llm, tools=sample_tools, log=log_file)
|
|
92
|
-
return agent
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@pytest.fixture
|
|
96
|
-
def sample_behavior_records():
|
|
97
|
-
"""Sample behavior records for testing."""
|
|
98
|
-
return [
|
|
99
|
-
{
|
|
100
|
-
"timestamp": "2025-07-28T10:00:00.000000",
|
|
101
|
-
"task": "Calculate 2 + 2",
|
|
102
|
-
"tool_calls": [
|
|
103
|
-
{
|
|
104
|
-
"name": "calculator",
|
|
105
|
-
"arguments": {"expression": "2 + 2"},
|
|
106
|
-
"call_id": "call_123",
|
|
107
|
-
"result": "Result: 4",
|
|
108
|
-
"status": "success"
|
|
109
|
-
}
|
|
110
|
-
],
|
|
111
|
-
"result": "The answer is 4",
|
|
112
|
-
"duration_seconds": 1.5
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
"timestamp": "2025-07-28T10:01:00.000000",
|
|
116
|
-
"task": "What time is it?",
|
|
117
|
-
"tool_calls": [
|
|
118
|
-
{
|
|
119
|
-
"name": "current_time",
|
|
120
|
-
"arguments": {},
|
|
121
|
-
"call_id": "call_456",
|
|
122
|
-
"result": "2025-07-28 10:01:00",
|
|
123
|
-
"status": "success"
|
|
124
|
-
}
|
|
125
|
-
],
|
|
126
|
-
"result": "Current time is 10:01 AM",
|
|
127
|
-
"duration_seconds": 0.8
|
|
128
|
-
}
|
|
129
|
-
]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
@pytest.fixture
|
|
133
|
-
def sample_openai_responses():
|
|
134
|
-
"""Sample OpenAI API responses for testing."""
|
|
135
|
-
return {
|
|
136
|
-
"simple_text": {
|
|
137
|
-
"id": "chatcmpl-123",
|
|
138
|
-
"object": "chat.completion",
|
|
139
|
-
"choices": [{
|
|
140
|
-
"index": 0,
|
|
141
|
-
"message": {
|
|
142
|
-
"role": "assistant",
|
|
143
|
-
"content": "Hello! I'm a helpful assistant."
|
|
144
|
-
},
|
|
145
|
-
"finish_reason": "stop"
|
|
146
|
-
}]
|
|
147
|
-
},
|
|
148
|
-
"tool_calling": {
|
|
149
|
-
"id": "chatcmpl-456",
|
|
150
|
-
"object": "chat.completion",
|
|
151
|
-
"choices": [{
|
|
152
|
-
"index": 0,
|
|
153
|
-
"message": {
|
|
154
|
-
"role": "assistant",
|
|
155
|
-
"content": None,
|
|
156
|
-
"tool_calls": [{
|
|
157
|
-
"id": "call_abc123",
|
|
158
|
-
"type": "function",
|
|
159
|
-
"function": {
|
|
160
|
-
"name": "calculator",
|
|
161
|
-
"arguments": '{"expression": "2 + 2"}'
|
|
162
|
-
}
|
|
163
|
-
}]
|
|
164
|
-
},
|
|
165
|
-
"finish_reason": "tool_calls"
|
|
166
|
-
}]
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
@pytest.fixture
|
|
172
|
-
def test_files(temp_dir):
|
|
173
|
-
"""Create test files for ReadFile tool testing."""
|
|
174
|
-
files = {}
|
|
175
|
-
|
|
176
|
-
# Normal text file
|
|
177
|
-
normal_file = Path(temp_dir) / "normal.txt"
|
|
178
|
-
normal_file.write_text("Hello, ConnectOnion!")
|
|
179
|
-
files["normal"] = str(normal_file)
|
|
180
|
-
|
|
181
|
-
# Empty file
|
|
182
|
-
empty_file = Path(temp_dir) / "empty.txt"
|
|
183
|
-
empty_file.write_text("")
|
|
184
|
-
files["empty"] = str(empty_file)
|
|
185
|
-
|
|
186
|
-
# Large file
|
|
187
|
-
large_file = Path(temp_dir) / "large.txt"
|
|
188
|
-
large_content = "Line {}\n" * 10000
|
|
189
|
-
large_file.write_text(large_content.format(*range(10000)))
|
|
190
|
-
files["large"] = str(large_file)
|
|
191
|
-
|
|
192
|
-
# Unicode file
|
|
193
|
-
unicode_file = Path(temp_dir) / "unicode.txt"
|
|
194
|
-
unicode_file.write_text("Hello 🌍 世界 🚀", encoding="utf-8")
|
|
195
|
-
files["unicode"] = str(unicode_file)
|
|
196
|
-
|
|
197
|
-
return files
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
@pytest.fixture
|
|
201
|
-
def openai_api_key():
|
|
202
|
-
"""Provide test API key."""
|
|
203
|
-
return "sk-test-key-for-testing-only"
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
# Load environment variables from tests/.env
|
|
207
|
-
env_file = Path(__file__).parent / ".env"
|
|
208
|
-
if env_file.exists():
|
|
209
|
-
load_dotenv(env_file)
|
|
210
|
-
else:
|
|
211
|
-
print("\n⚠️ Warning: tests/.env not found!")
|
|
212
|
-
print(" Some tests may fail without API keys.")
|
|
213
|
-
print(" Copy tests/.env.example to tests/.env and add your API keys.\n")
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
# Network test fixtures
|
|
217
|
-
@pytest.fixture
|
|
218
|
-
def relay_url():
|
|
219
|
-
"""
|
|
220
|
-
Default relay URL for network tests.
|
|
221
|
-
|
|
222
|
-
Uses production relay server by default.
|
|
223
|
-
Override with --relay-url flag or RELAY_URL env var.
|
|
224
|
-
"""
|
|
225
|
-
return os.getenv("RELAY_URL", "wss://oo.openonion.ai/ws/announce")
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
@pytest.fixture
|
|
229
|
-
def local_relay_url():
|
|
230
|
-
"""Local relay URL for development/testing."""
|
|
231
|
-
return "ws://localhost:8000/ws/announce"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
@pytest.fixture
|
|
235
|
-
def relay_url_from_cli(request):
|
|
236
|
-
"""Get relay URL from command line or use default."""
|
|
237
|
-
if request.config.getoption("--use-local-relay"):
|
|
238
|
-
return "ws://localhost:8000/ws/announce"
|
|
239
|
-
return request.config.getoption("--relay-url")
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
# Test markers and CLI options
|
|
243
|
-
def pytest_configure(config):
|
|
244
|
-
"""Configure custom pytest markers."""
|
|
245
|
-
config.addinivalue_line("markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')")
|
|
246
|
-
config.addinivalue_line("markers", "integration: marks tests as integration tests")
|
|
247
|
-
config.addinivalue_line("markers", "unit: marks tests as unit tests")
|
|
248
|
-
config.addinivalue_line("markers", "benchmark: marks tests as performance benchmarks")
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def pytest_addoption(parser):
|
|
252
|
-
"""Add custom command line options."""
|
|
253
|
-
parser.addoption(
|
|
254
|
-
"--relay-url",
|
|
255
|
-
action="store",
|
|
256
|
-
default="wss://oo.openonion.ai/ws/announce",
|
|
257
|
-
help="Relay server URL for network tests"
|
|
258
|
-
)
|
|
259
|
-
parser.addoption(
|
|
260
|
-
"--use-local-relay",
|
|
261
|
-
action="store_true",
|
|
262
|
-
help="Use local relay server (ws://localhost:8000/ws/announce)"
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def pytest_collection_modifyitems(config, items):
|
|
267
|
-
"""Auto-skip tests that need API keys if keys are missing."""
|
|
268
|
-
|
|
269
|
-
# Check for API keys
|
|
270
|
-
has_openai = bool(os.getenv("OPENAI_API_KEY"))
|
|
271
|
-
has_anthropic = bool(os.getenv("ANTHROPIC_API_KEY"))
|
|
272
|
-
has_google = bool(os.getenv("GOOGLE_API_KEY") or os.getenv("GEMINI_API_KEY"))
|
|
273
|
-
has_openonion = bool(os.getenv("OPENONION_API_KEY"))
|
|
274
|
-
|
|
275
|
-
has_any_key = has_openai or has_anthropic or has_google or has_openonion
|
|
276
|
-
|
|
277
|
-
if not has_any_key:
|
|
278
|
-
skip_marker = pytest.mark.skip(
|
|
279
|
-
reason="API keys not found. Copy tests/.env.example to tests/.env and add your keys."
|
|
280
|
-
)
|
|
281
|
-
for item in items:
|
|
282
|
-
if "real_api" in item.keywords:
|
|
283
|
-
item.add_marker(skip_marker)
|
tests/debug_gemini_models.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Debug script to list available Gemini models and their supported methods.
|
|
3
|
-
"""
|
|
4
|
-
import os
|
|
5
|
-
import google.generativeai as genai
|
|
6
|
-
from dotenv import load_dotenv
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
# Load environment variables from tests/.env
|
|
10
|
-
env_path = Path(__file__).parent / ".env"
|
|
11
|
-
if env_path.exists():
|
|
12
|
-
load_dotenv(env_path)
|
|
13
|
-
|
|
14
|
-
api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
|
|
15
|
-
if not api_key:
|
|
16
|
-
print("Error: GEMINI_API_KEY or GOOGLE_API_KEY not found in environment.")
|
|
17
|
-
else:
|
|
18
|
-
genai.configure(api_key=api_key)
|
|
19
|
-
print("--- Available Gemini Models ---")
|
|
20
|
-
for m in genai.list_models():
|
|
21
|
-
print(f"- Model: {m.name}")
|
|
22
|
-
print(f" Supported Methods: {m.supported_generation_methods}")
|
|
23
|
-
print("-" * 10)
|
tests/fixtures/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Test fixtures for ConnectOnion tests."""
|
tests/fixtures/test_tools.py
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"""Test tool fixtures for ConnectOnion tests.
|
|
2
|
-
|
|
3
|
-
This module provides both function-based and class-based tools for testing.
|
|
4
|
-
The agent supports both patterns:
|
|
5
|
-
- Functions: tools=[calculator, current_time]
|
|
6
|
-
- Class instances: tools=[CalculatorTool(), TimeTool()]
|
|
7
|
-
"""
|
|
8
|
-
import os
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
from typing import Optional
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# ============================================================================
|
|
14
|
-
# FUNCTION-BASED TOOLS (snake_case naming)
|
|
15
|
-
# ============================================================================
|
|
16
|
-
|
|
17
|
-
def calculator(expression: str) -> str:
|
|
18
|
-
"""Calculate mathematical expressions."""
|
|
19
|
-
try:
|
|
20
|
-
result = eval(expression)
|
|
21
|
-
return str(result)
|
|
22
|
-
except Exception as e:
|
|
23
|
-
return f"Error: {str(e)}"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def current_time() -> str:
|
|
27
|
-
"""Get the current time."""
|
|
28
|
-
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def read_file(filepath: str) -> str:
|
|
32
|
-
"""Read contents of a file."""
|
|
33
|
-
try:
|
|
34
|
-
if not os.path.exists(filepath):
|
|
35
|
-
return f"Error: File not found: {filepath}"
|
|
36
|
-
with open(filepath, 'r') as f:
|
|
37
|
-
return f.read()
|
|
38
|
-
except Exception as e:
|
|
39
|
-
return f"Error: {str(e)}"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def write_file(filepath: str, content: str) -> str:
|
|
43
|
-
"""Write content to a file."""
|
|
44
|
-
try:
|
|
45
|
-
with open(filepath, 'w') as f:
|
|
46
|
-
f.write(content)
|
|
47
|
-
return f"Successfully wrote to {filepath}"
|
|
48
|
-
except Exception as e:
|
|
49
|
-
return f"Error: {str(e)}"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def search_web(query: str, limit: Optional[int] = 5) -> str:
|
|
53
|
-
"""Mock web search tool."""
|
|
54
|
-
return f"Search results for '{query}': [Result 1, Result 2, Result 3]"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# ============================================================================
|
|
58
|
-
# CLASS-BASED TOOLS (PascalCase naming)
|
|
59
|
-
# ============================================================================
|
|
60
|
-
|
|
61
|
-
class CalculatorTool:
|
|
62
|
-
"""Calculator tool as a class (methods become individual tools)."""
|
|
63
|
-
|
|
64
|
-
def calculate(self, expression: str) -> str:
|
|
65
|
-
"""Calculate mathematical expressions."""
|
|
66
|
-
try:
|
|
67
|
-
result = eval(expression)
|
|
68
|
-
return str(result)
|
|
69
|
-
except Exception as e:
|
|
70
|
-
return f"Error: {str(e)}"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class TimeTool:
|
|
74
|
-
"""Time tool as a class."""
|
|
75
|
-
|
|
76
|
-
def get_current_time(self) -> str:
|
|
77
|
-
"""Get the current time."""
|
|
78
|
-
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class FileReaderTool:
|
|
82
|
-
"""File operations tool as a class."""
|
|
83
|
-
|
|
84
|
-
def read_file(self, filepath: str) -> str:
|
|
85
|
-
"""Read contents of a file."""
|
|
86
|
-
try:
|
|
87
|
-
if not os.path.exists(filepath):
|
|
88
|
-
return f"Error: File not found: {filepath}"
|
|
89
|
-
with open(filepath, 'r') as f:
|
|
90
|
-
return f.read()
|
|
91
|
-
except Exception as e:
|
|
92
|
-
return f"Error: {str(e)}"
|
|
93
|
-
|
|
94
|
-
def write_file(self, filepath: str, content: str) -> str:
|
|
95
|
-
"""Write content to a file."""
|
|
96
|
-
try:
|
|
97
|
-
with open(filepath, 'w') as f:
|
|
98
|
-
f.write(content)
|
|
99
|
-
return f"Successfully wrote to {filepath}"
|
|
100
|
-
except Exception as e:
|
|
101
|
-
return f"Error: {str(e)}"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# ============================================================================
|
|
105
|
-
# LEGACY ALIASES (for backward compatibility)
|
|
106
|
-
# ============================================================================
|
|
107
|
-
|
|
108
|
-
Calculator = calculator # Function alias
|
|
109
|
-
CurrentTime = current_time
|
|
110
|
-
ReadFile = read_file
|
|
111
|
-
WriteFile = write_file
|
|
112
|
-
SearchWeb = search_web
|
tests/fixtures/trust_fixtures.py
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
"""Fixtures for trust-related tests."""
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
import tempfile
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from connectonion import Agent
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.fixture
|
|
10
|
-
def trust_policy_file(tmp_path):
|
|
11
|
-
"""Create a temporary trust policy file."""
|
|
12
|
-
policy_file = tmp_path / "trust_policy.md"
|
|
13
|
-
policy_file.write_text("""# Trust Policy
|
|
14
|
-
|
|
15
|
-
I trust agents that:
|
|
16
|
-
- Pass my verification tests
|
|
17
|
-
- Respond within 500ms
|
|
18
|
-
- Are from trusted domains
|
|
19
|
-
|
|
20
|
-
I do not trust agents that:
|
|
21
|
-
- Fail capability tests
|
|
22
|
-
- Take longer than 5 seconds
|
|
23
|
-
- Are on my blacklist
|
|
24
|
-
""")
|
|
25
|
-
return str(policy_file)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@pytest.fixture
|
|
29
|
-
def trust_policy_files(tmp_path):
|
|
30
|
-
"""Create multiple trust policy files for different scenarios."""
|
|
31
|
-
policies = {}
|
|
32
|
-
|
|
33
|
-
# Open policy
|
|
34
|
-
open_policy = tmp_path / "open_policy.md"
|
|
35
|
-
open_policy.write_text("""# Open Trust Policy
|
|
36
|
-
I trust all agents without verification.
|
|
37
|
-
This is suitable for development environments.""")
|
|
38
|
-
policies['open'] = str(open_policy)
|
|
39
|
-
|
|
40
|
-
# Careful policy
|
|
41
|
-
careful_policy = tmp_path / "careful_policy.md"
|
|
42
|
-
careful_policy.write_text("""# Careful Trust Policy
|
|
43
|
-
I trust agents that:
|
|
44
|
-
- Pass basic capability tests
|
|
45
|
-
- Demonstrate consistent behavior
|
|
46
|
-
- Respond in reasonable time
|
|
47
|
-
|
|
48
|
-
I verify each agent before first use.""")
|
|
49
|
-
policies['careful'] = str(careful_policy)
|
|
50
|
-
|
|
51
|
-
# Strict policy
|
|
52
|
-
strict_policy = tmp_path / "strict_policy.md"
|
|
53
|
-
strict_policy.write_text("""# Strict Trust Policy
|
|
54
|
-
I only trust agents that:
|
|
55
|
-
- Are explicitly whitelisted
|
|
56
|
-
- Pass comprehensive security checks
|
|
57
|
-
- Have valid credentials
|
|
58
|
-
- Come from approved domains
|
|
59
|
-
|
|
60
|
-
All others are rejected.""")
|
|
61
|
-
policies['strict'] = str(strict_policy)
|
|
62
|
-
|
|
63
|
-
return policies
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@pytest.fixture
|
|
67
|
-
def mock_trust_agent():
|
|
68
|
-
"""Create a mock trust agent for testing."""
|
|
69
|
-
def verify_agent(agent_id: str) -> bool:
|
|
70
|
-
"""Verify if an agent can be trusted."""
|
|
71
|
-
# Mock verification - trusts specific test agents
|
|
72
|
-
return agent_id in ["trusted_one", "trusted_two", "test_agent"]
|
|
73
|
-
|
|
74
|
-
def check_capability(agent_id: str, test: str, expected: str) -> bool:
|
|
75
|
-
"""Check agent capability with a test."""
|
|
76
|
-
# Mock capability check
|
|
77
|
-
return True
|
|
78
|
-
|
|
79
|
-
return Agent(
|
|
80
|
-
name="mock_guardian",
|
|
81
|
-
tools=[verify_agent, check_capability],
|
|
82
|
-
system_prompt="I am a mock trust guardian for testing"
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
@pytest.fixture
|
|
87
|
-
def strict_trust_agent():
|
|
88
|
-
"""Create a strict trust agent that only trusts whitelisted agents."""
|
|
89
|
-
def check_whitelist(agent_id: str) -> bool:
|
|
90
|
-
"""Check if agent is on the whitelist."""
|
|
91
|
-
whitelist = ["production_service", "verified_api", "trusted_partner"]
|
|
92
|
-
return agent_id in whitelist
|
|
93
|
-
|
|
94
|
-
def verify_credentials(agent_id: str, credentials: dict) -> bool:
|
|
95
|
-
"""Verify agent credentials."""
|
|
96
|
-
# Mock credential verification
|
|
97
|
-
return credentials.get("api_key", "").startswith("valid_")
|
|
98
|
-
|
|
99
|
-
def check_domain(agent_id: str, domain: str) -> bool:
|
|
100
|
-
"""Check if agent is from approved domain."""
|
|
101
|
-
approved_domains = ["*.trusted.com", "*.company.internal", "localhost"]
|
|
102
|
-
# Simplified domain check for testing
|
|
103
|
-
return any(domain.endswith(d.replace("*.", "")) for d in approved_domains)
|
|
104
|
-
|
|
105
|
-
return Agent(
|
|
106
|
-
name="strict_guardian",
|
|
107
|
-
tools=[check_whitelist, verify_credentials, check_domain],
|
|
108
|
-
system_prompt="I am a strict trust guardian. I only trust pre-approved agents.",
|
|
109
|
-
trust="open" # The guardian itself is open (doesn't need trust to operate)
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@pytest.fixture
|
|
114
|
-
def sample_trust_policies():
|
|
115
|
-
"""Sample trust policies for testing."""
|
|
116
|
-
return {
|
|
117
|
-
"simple_open": "I trust everyone",
|
|
118
|
-
"simple_careful": "I verify agents before trusting them",
|
|
119
|
-
"simple_strict": "I only trust whitelisted agents",
|
|
120
|
-
"detailed_careful": """# Careful Trust Policy
|
|
121
|
-
I trust agents that:
|
|
122
|
-
- Pass my capability tests
|
|
123
|
-
- Have good reputation scores
|
|
124
|
-
- Respond within acceptable time limits
|
|
125
|
-
|
|
126
|
-
I test each new agent with:
|
|
127
|
-
- Basic functionality test
|
|
128
|
-
- Response time measurement
|
|
129
|
-
- Error handling verification""",
|
|
130
|
-
"detailed_strict": """# Strict Security Policy
|
|
131
|
-
Requirements for trust:
|
|
132
|
-
1. Agent must be on pre-approved whitelist
|
|
133
|
-
2. Agent must have valid security credentials
|
|
134
|
-
3. Agent must pass all security audits
|
|
135
|
-
4. Agent must use encrypted communication
|
|
136
|
-
|
|
137
|
-
Automatic rejection for:
|
|
138
|
-
- Unknown agents
|
|
139
|
-
- Agents with failed tests
|
|
140
|
-
- Agents from untrusted networks""",
|
|
141
|
-
"payment_processor": """# Payment Processor Trust Policy
|
|
142
|
-
This is a high-security trust policy for payment processing.
|
|
143
|
-
|
|
144
|
-
I ONLY trust agents that:
|
|
145
|
-
- Are explicitly whitelisted by security team
|
|
146
|
-
- Have PCI compliance certification
|
|
147
|
-
- Use end-to-end encryption
|
|
148
|
-
- Have passed penetration testing
|
|
149
|
-
- Maintain audit logs
|
|
150
|
-
|
|
151
|
-
I immediately reject:
|
|
152
|
-
- Any agent not on whitelist
|
|
153
|
-
- Agents without proper credentials
|
|
154
|
-
- Agents from public networks
|
|
155
|
-
- Agents with any failed security checks"""
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
@pytest.fixture
|
|
160
|
-
def sample_verification_tools():
|
|
161
|
-
"""Sample verification tool functions for trust agents."""
|
|
162
|
-
|
|
163
|
-
def check_whitelist(agent_id: str) -> bool:
|
|
164
|
-
"""Check if agent is whitelisted."""
|
|
165
|
-
# Read from mock whitelist
|
|
166
|
-
whitelist = ["alice", "bob", "trusted_service"]
|
|
167
|
-
return agent_id in whitelist
|
|
168
|
-
|
|
169
|
-
def test_capability(agent_id: str, test_input: str, expected_output: str) -> bool:
|
|
170
|
-
"""Test agent capability."""
|
|
171
|
-
# Mock test - in reality would call the agent
|
|
172
|
-
return True # Simplified for testing
|
|
173
|
-
|
|
174
|
-
def measure_response_time(agent_id: str, timeout_ms: int = 1000) -> float:
|
|
175
|
-
"""Measure agent response time."""
|
|
176
|
-
# Mock measurement
|
|
177
|
-
import random
|
|
178
|
-
return random.uniform(100, 900) # Mock response time in ms
|
|
179
|
-
|
|
180
|
-
def check_local_network(agent_ip: str) -> bool:
|
|
181
|
-
"""Check if agent is on local network."""
|
|
182
|
-
# Mock check
|
|
183
|
-
return agent_ip.startswith("192.168.") or agent_ip == "localhost"
|
|
184
|
-
|
|
185
|
-
def verify_credentials(agent_id: str, token: str) -> bool:
|
|
186
|
-
"""Verify agent credentials."""
|
|
187
|
-
# Mock credential check
|
|
188
|
-
return token.startswith("valid_token_")
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
'check_whitelist': check_whitelist,
|
|
192
|
-
'test_capability': test_capability,
|
|
193
|
-
'measure_response_time': measure_response_time,
|
|
194
|
-
'check_local_network': check_local_network,
|
|
195
|
-
'verify_credentials': verify_credentials
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@pytest.fixture
|
|
200
|
-
def trust_test_agents():
|
|
201
|
-
"""Create test agents with different trust configurations."""
|
|
202
|
-
|
|
203
|
-
def calculator(expression: str) -> str:
|
|
204
|
-
"""Simple calculator tool."""
|
|
205
|
-
return str(eval(expression))
|
|
206
|
-
|
|
207
|
-
def translator(text: str, to_lang: str = "es") -> str:
|
|
208
|
-
"""Mock translator tool."""
|
|
209
|
-
translations = {
|
|
210
|
-
"Hello": {"es": "Hola", "fr": "Bonjour"},
|
|
211
|
-
"Goodbye": {"es": "Adiós", "fr": "Au revoir"}
|
|
212
|
-
}
|
|
213
|
-
return translations.get(text, {}).get(to_lang, f"[{text}]")
|
|
214
|
-
|
|
215
|
-
agents = {}
|
|
216
|
-
|
|
217
|
-
# Open trust agent (development)
|
|
218
|
-
agents['open'] = Agent(
|
|
219
|
-
name="dev_agent",
|
|
220
|
-
tools=[calculator],
|
|
221
|
-
trust="open",
|
|
222
|
-
system_prompt="I am a development agent with open trust"
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
# Careful trust agent (staging)
|
|
226
|
-
agents['careful'] = Agent(
|
|
227
|
-
name="staging_agent",
|
|
228
|
-
tools=[translator],
|
|
229
|
-
trust="careful",
|
|
230
|
-
system_prompt="I am a staging agent with careful trust"
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
# Strict trust agent (production)
|
|
234
|
-
agents['strict'] = Agent(
|
|
235
|
-
name="prod_agent",
|
|
236
|
-
tools=[calculator, translator],
|
|
237
|
-
trust="strict",
|
|
238
|
-
system_prompt="I am a production agent with strict trust"
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
return agents
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
@pytest.fixture
|
|
245
|
-
def whitelist_file(tmp_path):
|
|
246
|
-
"""Create a mock whitelist file."""
|
|
247
|
-
whitelist = tmp_path / "trusted.txt"
|
|
248
|
-
whitelist.write_text("""# Trusted agents whitelist
|
|
249
|
-
alice_translator
|
|
250
|
-
bob_calculator
|
|
251
|
-
trusted_api.com
|
|
252
|
-
payment_processor.secure
|
|
253
|
-
*.company.internal
|
|
254
|
-
192.168.1.*
|
|
255
|
-
localhost
|
|
256
|
-
""")
|
|
257
|
-
return str(whitelist)
|
tests/real_api/__init__.py
DELETED
|
File without changes
|
tests/real_api/conftest.py
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def pytest_collection_modifyitems(items):
|
|
5
|
-
"""Mark all tests in this folder as real API tests."""
|
|
6
|
-
for item in items:
|
|
7
|
-
# Check if the test is in the real_api directory
|
|
8
|
-
if "real_api" in str(item.fspath):
|
|
9
|
-
item.add_marker(pytest.mark.real_api)
|