connectonion 0.4.12__py3-none-any.whl → 0.5.1__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 +13 -9
- 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 +14 -56
- connectonion/useful_plugins/shell_approval.py +2 -2
- connectonion/useful_tools/memory.py +4 -0
- {connectonion-0.4.12.dist-info → connectonion-0.5.1.dist-info}/METADATA +49 -49
- {connectonion-0.4.12.dist-info → connectonion-0.5.1.dist-info}/RECORD +27 -71
- {connectonion-0.4.12.dist-info → connectonion-0.5.1.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.12.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-0.4.12.dist-info → connectonion-0.5.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""Test connectonion client against production API"""
|
|
2
|
-
import os
|
|
3
|
-
import time
|
|
4
|
-
import requests
|
|
5
|
-
from pydantic import BaseModel
|
|
6
|
-
from nacl.signing import SigningKey
|
|
7
|
-
from nacl.encoding import HexEncoder
|
|
8
|
-
from connectonion import llm_do
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Production URL
|
|
12
|
-
PRODUCTION_URL = "http://3.24.102.245"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class EmailDraft(BaseModel):
|
|
16
|
-
"""Email draft with subject and body"""
|
|
17
|
-
subject: str
|
|
18
|
-
body: str
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def get_test_token():
|
|
22
|
-
"""Generate test auth token for production backend"""
|
|
23
|
-
signing_key = SigningKey.generate()
|
|
24
|
-
public_key = "0x" + signing_key.verify_key.encode(encoder=HexEncoder).decode()
|
|
25
|
-
timestamp = int(time.time())
|
|
26
|
-
message = f"ConnectOnion-Auth-{public_key}-{timestamp}"
|
|
27
|
-
signature = signing_key.sign(message.encode()).signature.hex()
|
|
28
|
-
|
|
29
|
-
response = requests.post(
|
|
30
|
-
f"{PRODUCTION_URL}/api/v1/auth",
|
|
31
|
-
json={
|
|
32
|
-
"public_key": public_key,
|
|
33
|
-
"message": message,
|
|
34
|
-
"signature": signature
|
|
35
|
-
}
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
if response.status_code != 200:
|
|
39
|
-
raise Exception(f"Auth failed: {response.status_code} - {response.text}")
|
|
40
|
-
|
|
41
|
-
return response.json()["token"]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def test_production_llm_do_structured():
|
|
45
|
-
"""Test llm_do() with structured output against production API"""
|
|
46
|
-
print("\n" + "=" * 80)
|
|
47
|
-
print("TESTING CONNECTONION CLIENT AGAINST PRODUCTION")
|
|
48
|
-
print(f"Target: {PRODUCTION_URL}")
|
|
49
|
-
print("=" * 80)
|
|
50
|
-
|
|
51
|
-
# Point to production instead of localhost
|
|
52
|
-
os.environ["OPENONION_API_URL"] = PRODUCTION_URL
|
|
53
|
-
|
|
54
|
-
# Get auth token from production
|
|
55
|
-
token = get_test_token()
|
|
56
|
-
os.environ["OPENONION_API_KEY"] = token
|
|
57
|
-
|
|
58
|
-
print("\n✓ Authentication successful")
|
|
59
|
-
print(f" Using production API: {PRODUCTION_URL}")
|
|
60
|
-
|
|
61
|
-
# Test structured output with co/ model
|
|
62
|
-
print("\nTesting llm_do() with structured output (EmailDraft)...")
|
|
63
|
-
draft = llm_do(
|
|
64
|
-
"Write a friendly hello email to a new colleague",
|
|
65
|
-
output=EmailDraft,
|
|
66
|
-
temperature=0.7,
|
|
67
|
-
model="co/gpt-4o-mini"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# Verify we got a valid Pydantic model
|
|
71
|
-
assert isinstance(draft, EmailDraft)
|
|
72
|
-
assert isinstance(draft.subject, str)
|
|
73
|
-
assert isinstance(draft.body, str)
|
|
74
|
-
assert len(draft.subject) > 0
|
|
75
|
-
assert len(draft.body) > 0
|
|
76
|
-
|
|
77
|
-
print("\n✓ Structured output test PASSED!")
|
|
78
|
-
print(f" Subject: {draft.subject}")
|
|
79
|
-
print(f" Body preview: {draft.body[:150]}...")
|
|
80
|
-
|
|
81
|
-
print("\n" + "=" * 80)
|
|
82
|
-
print("✅ CONNECTONION CLIENT WORKS WITH PRODUCTION API!")
|
|
83
|
-
print(" Issue #9 is fully resolved in production")
|
|
84
|
-
print("=" * 80)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if __name__ == "__main__":
|
|
88
|
-
try:
|
|
89
|
-
test_production_llm_do_structured()
|
|
90
|
-
except Exception as e:
|
|
91
|
-
print(f"\n❌ Production client test failed: {e}")
|
|
92
|
-
import traceback
|
|
93
|
-
traceback.print_exc()
|
|
94
|
-
exit(1)
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Real Anthropic API tests.
|
|
3
|
-
|
|
4
|
-
These tests make actual API calls to Anthropic and cost real money.
|
|
5
|
-
Run with: pytest test_real_anthropic.py -v
|
|
6
|
-
|
|
7
|
-
Requires: ANTHROPIC_API_KEY environment variable
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
import pytest
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from dotenv import load_dotenv
|
|
14
|
-
from connectonion import Agent
|
|
15
|
-
from connectonion.llm import AnthropicLLM
|
|
16
|
-
|
|
17
|
-
# Load environment variables from tests/.env
|
|
18
|
-
env_path = Path(__file__).parent / ".env"
|
|
19
|
-
if env_path.exists():
|
|
20
|
-
load_dotenv(env_path)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def text_processor(text: str) -> str:
|
|
24
|
-
"""Simple text processor for testing."""
|
|
25
|
-
return f"Processed: {text.upper()}"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class TestRealAnthropic:
|
|
29
|
-
"""Test real Anthropic API integration."""
|
|
30
|
-
|
|
31
|
-
def test_anthropic_basic_completion(self):
|
|
32
|
-
"""Test basic completion with Anthropic."""
|
|
33
|
-
llm = AnthropicLLM(model="claude-3-5-haiku-latest")
|
|
34
|
-
agent = Agent(name="anthropic_test", llm=llm)
|
|
35
|
-
|
|
36
|
-
response = agent.input("Say 'Hello from Claude' exactly")
|
|
37
|
-
assert response is not None
|
|
38
|
-
assert "Hello from Claude" in response
|
|
39
|
-
|
|
40
|
-
def test_anthropic_with_tools(self):
|
|
41
|
-
"""Test Anthropic with tool calling."""
|
|
42
|
-
agent = Agent(
|
|
43
|
-
name="anthropic_tools",
|
|
44
|
-
model="claude-3-5-haiku-latest",
|
|
45
|
-
tools=[text_processor]
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
response = agent.input("Process the text 'hello world'")
|
|
49
|
-
assert response is not None
|
|
50
|
-
assert "HELLO WORLD" in response or "processed" in response.lower()
|
|
51
|
-
|
|
52
|
-
def test_anthropic_multi_turn(self):
|
|
53
|
-
"""Test multi-turn conversation with Anthropic."""
|
|
54
|
-
agent = Agent(
|
|
55
|
-
name="anthropic_conversation",
|
|
56
|
-
model="claude-3-5-haiku-latest"
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
# First turn
|
|
60
|
-
response = agent.input("I'm learning Python. Remember this.")
|
|
61
|
-
assert response is not None
|
|
62
|
-
|
|
63
|
-
# Second turn - should remember context
|
|
64
|
-
response = agent.input("What programming language am I learning?")
|
|
65
|
-
assert response is not None
|
|
66
|
-
assert "Python" in response
|
|
67
|
-
|
|
68
|
-
def test_anthropic_different_models(self):
|
|
69
|
-
"""Test different Anthropic models."""
|
|
70
|
-
models = [
|
|
71
|
-
"claude-3-5-haiku-latest",
|
|
72
|
-
"claude-3-5-sonnet-latest",
|
|
73
|
-
]
|
|
74
|
-
|
|
75
|
-
for model in models:
|
|
76
|
-
if "opus" in model and not os.getenv("TEST_EXPENSIVE_MODELS"):
|
|
77
|
-
continue # Skip expensive models unless explicitly enabled
|
|
78
|
-
|
|
79
|
-
agent = Agent(
|
|
80
|
-
name=f"anthropic_{model.split('-')[2]}",
|
|
81
|
-
model=model
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
response = agent.input("Reply with OK")
|
|
85
|
-
assert response is not None
|
|
86
|
-
assert len(response) > 0
|
|
87
|
-
|
|
88
|
-
def test_anthropic_system_prompt(self):
|
|
89
|
-
"""Test Anthropic with custom system prompt."""
|
|
90
|
-
agent = Agent(
|
|
91
|
-
name="anthropic_system",
|
|
92
|
-
model="claude-3-5-haiku-latest",
|
|
93
|
-
system_prompt="You are a helpful poetry assistant. Always respond in haiku format."
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
response = agent.input("Tell me about the weather")
|
|
97
|
-
assert response is not None
|
|
98
|
-
# Check it's attempting haiku format (3 lines, roughly)
|
|
99
|
-
lines = response.strip().split('\n')
|
|
100
|
-
assert len(lines) >= 1 # At least has content
|
tests/real_api/test_real_api.py
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Test with the actual deployed OpenOnion API."""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
import requests
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from dotenv import load_dotenv
|
|
9
|
-
|
|
10
|
-
# Load test environment
|
|
11
|
-
env_path = Path(__file__).parent / '.env.test'
|
|
12
|
-
if env_path.exists():
|
|
13
|
-
load_dotenv(env_path)
|
|
14
|
-
|
|
15
|
-
def test_deployed_api():
|
|
16
|
-
"""Test the actual deployed API endpoints."""
|
|
17
|
-
|
|
18
|
-
print("\n" + "="*60)
|
|
19
|
-
print("🌐 TESTING DEPLOYED OPENONION API")
|
|
20
|
-
print("="*60)
|
|
21
|
-
|
|
22
|
-
backend_url = "https://oo.openonion.ai"
|
|
23
|
-
|
|
24
|
-
# Test 1: Health Check
|
|
25
|
-
print("\n✅ Testing health endpoint...")
|
|
26
|
-
response = requests.get(f"{backend_url}/health")
|
|
27
|
-
if response.status_code == 200:
|
|
28
|
-
data = response.json()
|
|
29
|
-
print(f" Status: {data.get('status')}")
|
|
30
|
-
print(f" Timestamp: {data.get('timestamp')}")
|
|
31
|
-
|
|
32
|
-
# Test 2: Check available email endpoints
|
|
33
|
-
print("\n📋 Available email endpoints:")
|
|
34
|
-
endpoints = [
|
|
35
|
-
"/api/email/send",
|
|
36
|
-
"/api/email/webhook/incoming",
|
|
37
|
-
"/api/email/tier",
|
|
38
|
-
"/api/email/upgrade",
|
|
39
|
-
"/api/email/received", # Admin endpoint
|
|
40
|
-
"/api/emails", # New endpoint (if deployed)
|
|
41
|
-
"/api/emails/mark-read" # New endpoint (if deployed)
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
for endpoint in endpoints:
|
|
45
|
-
# Try OPTIONS request to check if endpoint exists
|
|
46
|
-
try:
|
|
47
|
-
response = requests.options(f"{backend_url}{endpoint}", timeout=2)
|
|
48
|
-
if response.status_code < 500:
|
|
49
|
-
print(f" ✅ {endpoint} - Available")
|
|
50
|
-
else:
|
|
51
|
-
print(f" ❌ {endpoint} - Server error")
|
|
52
|
-
except:
|
|
53
|
-
print(f" ⚠️ {endpoint} - Not responding")
|
|
54
|
-
|
|
55
|
-
# Test 3: Try to authenticate
|
|
56
|
-
print("\n🔐 Testing authentication...")
|
|
57
|
-
|
|
58
|
-
# Note: The deployed API might not accept test signatures
|
|
59
|
-
auth_data = {
|
|
60
|
-
"public_key": os.getenv("TEST_PUBLIC_KEY"),
|
|
61
|
-
"message": "test_message",
|
|
62
|
-
"signature": "test_signature"
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
response = requests.post(f"{backend_url}/auth", json=auth_data)
|
|
66
|
-
if response.status_code == 200:
|
|
67
|
-
print(" ✅ Authentication successful")
|
|
68
|
-
token = response.json().get("token")
|
|
69
|
-
if token:
|
|
70
|
-
print(f" Token: {token[:30]}...")
|
|
71
|
-
else:
|
|
72
|
-
print(f" ⚠️ Authentication failed: {response.status_code}")
|
|
73
|
-
if response.status_code == 400:
|
|
74
|
-
print(" Note: Test signatures not accepted in production")
|
|
75
|
-
|
|
76
|
-
# Test 4: Check if new endpoints are deployed
|
|
77
|
-
print("\n🆕 Checking new email endpoints...")
|
|
78
|
-
|
|
79
|
-
jwt_token = os.getenv("TEST_JWT_TOKEN")
|
|
80
|
-
headers = {
|
|
81
|
-
"Authorization": f"Bearer {jwt_token}",
|
|
82
|
-
"Content-Type": "application/json"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# Try GET /api/emails
|
|
86
|
-
response = requests.get(f"{backend_url}/api/emails", headers=headers)
|
|
87
|
-
if response.status_code == 404:
|
|
88
|
-
print(" ℹ️ GET /api/emails - Not deployed yet")
|
|
89
|
-
print(" The new email inbox endpoints need to be deployed to production")
|
|
90
|
-
elif response.status_code == 200:
|
|
91
|
-
print(" ✅ GET /api/emails - Working!")
|
|
92
|
-
data = response.json()
|
|
93
|
-
print(f" Emails: {len(data.get('emails', []))}")
|
|
94
|
-
else:
|
|
95
|
-
print(f" ⚠️ GET /api/emails - Status: {response.status_code}")
|
|
96
|
-
|
|
97
|
-
# Summary
|
|
98
|
-
print("\n" + "="*60)
|
|
99
|
-
print("📊 SUMMARY")
|
|
100
|
-
print("="*60)
|
|
101
|
-
print("\nThe deployed API has the following email capabilities:")
|
|
102
|
-
print(" ✅ Send emails via /api/email/send")
|
|
103
|
-
print(" ✅ Receive emails via webhook")
|
|
104
|
-
print(" ✅ Email tier management")
|
|
105
|
-
print("\nThe new inbox features (get_emails, mark_read) are:")
|
|
106
|
-
print(" ⚠️ Implemented locally but not yet deployed")
|
|
107
|
-
print("\nNext steps:")
|
|
108
|
-
print(" 1. Deploy the updated oo-api with new endpoints")
|
|
109
|
-
print(" 2. Test with real authentication flow")
|
|
110
|
-
print(" 3. Verify email retrieval and marking as read")
|
|
111
|
-
|
|
112
|
-
if __name__ == "__main__":
|
|
113
|
-
test_deployed_api()
|
tests/real_api/test_real_auth.py
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"""End-to-end test for ConnectOnion authentication with production API."""
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
import json
|
|
5
|
-
import requests
|
|
6
|
-
from nacl.signing import SigningKey
|
|
7
|
-
from nacl.encoding import HexEncoder
|
|
8
|
-
|
|
9
|
-
def test_production_auth():
|
|
10
|
-
"""Test authentication against production API."""
|
|
11
|
-
|
|
12
|
-
# Generate test keys
|
|
13
|
-
signing_key = SigningKey.generate()
|
|
14
|
-
public_key = "0x" + signing_key.verify_key.encode(encoder=HexEncoder).decode()
|
|
15
|
-
|
|
16
|
-
# Create ConnectOnion format message
|
|
17
|
-
timestamp = int(time.time())
|
|
18
|
-
message = f"ConnectOnion-Auth-{public_key}-{timestamp}"
|
|
19
|
-
|
|
20
|
-
# Sign the message
|
|
21
|
-
signature = signing_key.sign(message.encode()).signature.hex()
|
|
22
|
-
|
|
23
|
-
# Prepare request exactly as CLI does
|
|
24
|
-
payload = {
|
|
25
|
-
"public_key": public_key,
|
|
26
|
-
"message": message,
|
|
27
|
-
"signature": signature
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
print("Testing Production API Authentication")
|
|
31
|
-
print("="*50)
|
|
32
|
-
print(f"Public Key: {public_key[:20]}...")
|
|
33
|
-
print(f"Message: {message[:50]}...")
|
|
34
|
-
print(f"Timestamp: {timestamp}")
|
|
35
|
-
|
|
36
|
-
# Test against production API
|
|
37
|
-
api_url = "https://oo.openonion.ai/auth"
|
|
38
|
-
|
|
39
|
-
try:
|
|
40
|
-
print(f"\nCalling: POST {api_url}")
|
|
41
|
-
response = requests.post(
|
|
42
|
-
api_url,
|
|
43
|
-
json=payload,
|
|
44
|
-
timeout=10,
|
|
45
|
-
headers={"Content-Type": "application/json"}
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
print(f"Response Status: {response.status_code}")
|
|
49
|
-
|
|
50
|
-
if response.status_code == 200:
|
|
51
|
-
data = response.json()
|
|
52
|
-
print("✅ Authentication successful!")
|
|
53
|
-
print(f" Token: {data.get('token', '')[:20]}...")
|
|
54
|
-
print(f" Public Key Confirmed: {data.get('public_key', '')[:20]}...")
|
|
55
|
-
|
|
56
|
-
# Test token validation
|
|
57
|
-
if 'token' in data:
|
|
58
|
-
validate_url = "https://oo.openonion.ai/api/validate"
|
|
59
|
-
val_response = requests.get(
|
|
60
|
-
validate_url,
|
|
61
|
-
headers={"Authorization": f"Bearer {data['token']}"},
|
|
62
|
-
timeout=5
|
|
63
|
-
)
|
|
64
|
-
if val_response.status_code == 200:
|
|
65
|
-
print("✅ Token validation successful!")
|
|
66
|
-
else:
|
|
67
|
-
print(f"⚠️ Token validation failed: {val_response.status_code}")
|
|
68
|
-
|
|
69
|
-
return True
|
|
70
|
-
else:
|
|
71
|
-
print(f"❌ Authentication failed: {response.status_code}")
|
|
72
|
-
print(f" Response: {response.text[:200]}")
|
|
73
|
-
return False
|
|
74
|
-
|
|
75
|
-
except requests.exceptions.ConnectionError as e:
|
|
76
|
-
print(f"❌ Connection failed: {e}")
|
|
77
|
-
return False
|
|
78
|
-
except Exception as e:
|
|
79
|
-
print(f"❌ Unexpected error: {e}")
|
|
80
|
-
return False
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def test_llm_models_endpoint():
|
|
84
|
-
"""Test that LLM models endpoint is accessible."""
|
|
85
|
-
|
|
86
|
-
print("\n\nTesting LLM Models Endpoint")
|
|
87
|
-
print("="*50)
|
|
88
|
-
|
|
89
|
-
api_url = "https://oo.openonion.ai/api/llm/models"
|
|
90
|
-
|
|
91
|
-
try:
|
|
92
|
-
response = requests.get(api_url, timeout=5)
|
|
93
|
-
print(f"Response Status: {response.status_code}")
|
|
94
|
-
|
|
95
|
-
if response.status_code == 200:
|
|
96
|
-
data = response.json()
|
|
97
|
-
models = data.get("models", [])
|
|
98
|
-
print(f"✅ Found {len(models)} models")
|
|
99
|
-
|
|
100
|
-
# Check for co/ models
|
|
101
|
-
co_models = [m for m in models if m.get("id", "").startswith("co/")]
|
|
102
|
-
if co_models:
|
|
103
|
-
print(f" Co/ models available: {len(co_models)}")
|
|
104
|
-
for model in co_models[:3]: # Show first 3
|
|
105
|
-
print(f" - {model.get('id')}: {model.get('name')}")
|
|
106
|
-
return True
|
|
107
|
-
else:
|
|
108
|
-
print(f"❌ Failed to get models: {response.status_code}")
|
|
109
|
-
return False
|
|
110
|
-
|
|
111
|
-
except Exception as e:
|
|
112
|
-
print(f"❌ Error: {e}")
|
|
113
|
-
return False
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if __name__ == "__main__":
|
|
117
|
-
print("ConnectOnion <-> OpenOnion Production API Test\n")
|
|
118
|
-
|
|
119
|
-
auth_ok = test_production_auth()
|
|
120
|
-
models_ok = test_llm_models_endpoint()
|
|
121
|
-
|
|
122
|
-
print("\n" + "="*50)
|
|
123
|
-
print("Test Results:")
|
|
124
|
-
print(f" Authentication: {'✅ PASSED' if auth_ok else '❌ FAILED'}")
|
|
125
|
-
print(f" LLM Models: {'✅ PASSED' if models_ok else '❌ FAILED'}")
|
|
126
|
-
|
|
127
|
-
if auth_ok and models_ok:
|
|
128
|
-
print("\n🎉 All tests passed! Integration is working.")
|
|
129
|
-
else:
|
|
130
|
-
print("\n⚠️ Some tests failed. Check the logs above.")
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Test email functionality with live backend - uses environment variables safely."""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
# Add parent to path
|
|
9
|
-
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
10
|
-
|
|
11
|
-
from connectonion import send_email, get_emails, mark_read
|
|
12
|
-
from tests.utils.config_helpers import ProjectHelper
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_live_email():
|
|
16
|
-
"""Test with deployed backend using environment variables."""
|
|
17
|
-
|
|
18
|
-
print("\n🧪 Live Email Test")
|
|
19
|
-
print("=" * 50)
|
|
20
|
-
|
|
21
|
-
# Use environment variables - NEVER hardcode credentials!
|
|
22
|
-
backend_url = os.getenv("CONNECTONION_BACKEND_URL", "https://oo.openonion.ai")
|
|
23
|
-
|
|
24
|
-
# Check if we have required environment variables
|
|
25
|
-
if not os.getenv("OPENAI_API_KEY"):
|
|
26
|
-
print("⚠️ Please set OPENAI_API_KEY environment variable")
|
|
27
|
-
print(" export OPENAI_API_KEY=your-key-here")
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
print(f"🌐 Using backend: {backend_url}")
|
|
31
|
-
|
|
32
|
-
with ProjectHelper() as project_dir:
|
|
33
|
-
print(f"📁 Test project: {project_dir}")
|
|
34
|
-
|
|
35
|
-
# The test project already has the test account configured
|
|
36
|
-
# Now test the functionality
|
|
37
|
-
|
|
38
|
-
# Test 1: Check current inbox
|
|
39
|
-
print("\n📥 Checking inbox...")
|
|
40
|
-
emails = get_emails()
|
|
41
|
-
|
|
42
|
-
if emails:
|
|
43
|
-
print(f"✅ Found {len(emails)} emails")
|
|
44
|
-
for i, email in enumerate(emails[:3], 1):
|
|
45
|
-
print(f"\n Email {i}:")
|
|
46
|
-
print(f" From: {email.get('from')}")
|
|
47
|
-
print(f" Subject: {email.get('subject')}")
|
|
48
|
-
print(f" Read: {email.get('read')}")
|
|
49
|
-
else:
|
|
50
|
-
print("📭 No emails in inbox")
|
|
51
|
-
|
|
52
|
-
# Test 2: Send a test email
|
|
53
|
-
print("\n📤 Sending test email...")
|
|
54
|
-
result = send_email(
|
|
55
|
-
to="test@example.com",
|
|
56
|
-
subject="Test from ConnectOnion",
|
|
57
|
-
message="This is a test email sent via the ConnectOnion API"
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
if result.get("success"):
|
|
61
|
-
print(f"✅ Email sent successfully!")
|
|
62
|
-
print(f" Message ID: {result.get('message_id')}")
|
|
63
|
-
print(f" From: {result.get('from')}")
|
|
64
|
-
else:
|
|
65
|
-
print(f"⚠️ Send failed: {result.get('error')}")
|
|
66
|
-
|
|
67
|
-
# Test 3: Mark emails as read
|
|
68
|
-
if emails and len(emails) > 0:
|
|
69
|
-
unread = [e for e in emails if not e.get('read')]
|
|
70
|
-
if unread:
|
|
71
|
-
print(f"\n✔️ Marking {len(unread)} emails as read...")
|
|
72
|
-
for email in unread[:3]: # Mark up to 3
|
|
73
|
-
success = mark_read(email['id'])
|
|
74
|
-
if success:
|
|
75
|
-
print(f" ✅ Marked {email['id']} as read")
|
|
76
|
-
else:
|
|
77
|
-
print(f" ⚠️ Failed to mark {email['id']}")
|
|
78
|
-
|
|
79
|
-
print("\n" + "=" * 50)
|
|
80
|
-
print("✅ Test completed")
|
|
81
|
-
print("\n⚠️ Remember: NEVER commit API keys to version control!")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if __name__ == "__main__":
|
|
85
|
-
# Safety check
|
|
86
|
-
if len(sys.argv) > 1 and sys.argv[1] == "--confirm":
|
|
87
|
-
test_live_email()
|
|
88
|
-
else:
|
|
89
|
-
print("⚠️ This test connects to the live backend.")
|
|
90
|
-
print(" Run with --confirm flag to proceed:")
|
|
91
|
-
print(" python test_email_live.py --confirm")
|
|
92
|
-
print("\n📝 Required environment variables:")
|
|
93
|
-
print(" - OPENAI_API_KEY")
|
|
94
|
-
print(" - CONNECTONION_BACKEND_URL (optional)")
|
|
95
|
-
print("\n🔒 Security: Use environment variables, never hardcode keys!")
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Real Google Gemini API tests.
|
|
3
|
-
|
|
4
|
-
These tests make actual API calls to Google Gemini and cost real money.
|
|
5
|
-
Run with: pytest test_real_gemini.py -v
|
|
6
|
-
|
|
7
|
-
Requires: GEMINI_API_KEY environment variable (GOOGLE_API_KEY also supported for backward compatibility)
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
import pytest
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from dotenv import load_dotenv
|
|
14
|
-
from connectonion import Agent
|
|
15
|
-
from connectonion.llm import GeminiLLM
|
|
16
|
-
|
|
17
|
-
# Load environment variables from tests/.env
|
|
18
|
-
env_path = Path(__file__).parent / ".env"
|
|
19
|
-
if env_path.exists():
|
|
20
|
-
load_dotenv(env_path)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def word_counter(text: str) -> str:
|
|
24
|
-
"""Count words in text for testing."""
|
|
25
|
-
words = text.split()
|
|
26
|
-
return f"Word count: {len(words)}"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class TestRealGemini:
|
|
30
|
-
"""Test real Google Gemini API integration."""
|
|
31
|
-
|
|
32
|
-
def test_gemini_basic_completion(self):
|
|
33
|
-
"""Test basic completion with Gemini."""
|
|
34
|
-
api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
|
|
35
|
-
llm = GeminiLLM(api_key=api_key, model="gemini-2.5-pro")
|
|
36
|
-
agent = Agent(name="gemini_test", llm=llm)
|
|
37
|
-
|
|
38
|
-
response = agent.input("Say 'Hello from Gemini' exactly")
|
|
39
|
-
assert response is not None
|
|
40
|
-
assert "Hello from Gemini" in response
|
|
41
|
-
|
|
42
|
-
def test_gemini_with_tools(self):
|
|
43
|
-
"""Test Gemini with tool calling."""
|
|
44
|
-
agent = Agent(
|
|
45
|
-
name="gemini_tools",
|
|
46
|
-
model="gemini-2.5-pro",
|
|
47
|
-
tools=[word_counter]
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
response = agent.input("Count the words in 'The quick brown fox'")
|
|
51
|
-
assert response is not None
|
|
52
|
-
assert "4" in response or "four" in response.lower()
|
|
53
|
-
|
|
54
|
-
def test_gemini_multi_turn(self):
|
|
55
|
-
"""Test multi-turn conversation with Gemini."""
|
|
56
|
-
agent = Agent(
|
|
57
|
-
name="gemini_conversation",
|
|
58
|
-
model="gemini-2.5-pro"
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
# First turn
|
|
62
|
-
response = agent.input("My favorite color is blue. Remember this.")
|
|
63
|
-
assert response is not None
|
|
64
|
-
|
|
65
|
-
# Second turn - should remember context
|
|
66
|
-
response = agent.input("What's my favorite color?")
|
|
67
|
-
assert response is not None
|
|
68
|
-
assert "blue" in response.lower()
|
|
69
|
-
|
|
70
|
-
def test_gemini_different_models(self):
|
|
71
|
-
"""Test different Gemini models."""
|
|
72
|
-
models = ["gemini-2.5-pro", "gemini-2.5-flash"]
|
|
73
|
-
|
|
74
|
-
for model in models:
|
|
75
|
-
|
|
76
|
-
api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
|
|
77
|
-
agent = Agent(
|
|
78
|
-
name=f"gemini_{model.replace('.', '_').replace('-', '_')}",
|
|
79
|
-
llm=GeminiLLM(api_key=api_key, model=model)
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
response = agent.input("Reply with OK")
|
|
83
|
-
assert response is not None
|
|
84
|
-
assert len(response) > 0
|
|
85
|
-
|
|
86
|
-
def test_gemini_system_prompt(self):
|
|
87
|
-
"""Test Gemini with custom system prompt."""
|
|
88
|
-
agent = Agent(
|
|
89
|
-
name="gemini_system",
|
|
90
|
-
model="gemini-2.5-pro",
|
|
91
|
-
system_prompt="You are a helpful math tutor. Always explain your reasoning step by step."
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
response = agent.input("What is 2 + 2?")
|
|
95
|
-
assert response is not None
|
|
96
|
-
assert "4" in response or "four" in response.lower()
|