epsimo-agent 0.1.0
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.
- package/LICENSE +21 -0
- package/SKILL.md +85 -0
- package/assets/example_asset.txt +24 -0
- package/epsimo/__init__.py +3 -0
- package/epsimo/__main__.py +4 -0
- package/epsimo/auth.py +143 -0
- package/epsimo/cli.py +586 -0
- package/epsimo/client.py +53 -0
- package/epsimo/resources/assistants.py +47 -0
- package/epsimo/resources/credits.py +16 -0
- package/epsimo/resources/db.py +31 -0
- package/epsimo/resources/files.py +39 -0
- package/epsimo/resources/projects.py +30 -0
- package/epsimo/resources/threads.py +83 -0
- package/epsimo/templates/components/AuthModal/AuthModal.module.css +39 -0
- package/epsimo/templates/components/AuthModal/AuthModal.tsx +138 -0
- package/epsimo/templates/components/BuyCredits/BuyCreditsModal.module.css +96 -0
- package/epsimo/templates/components/BuyCredits/BuyCreditsModal.tsx +132 -0
- package/epsimo/templates/components/BuyCredits/CreditsDisplay.tsx +101 -0
- package/epsimo/templates/components/ThreadChat/ThreadChat.module.css +551 -0
- package/epsimo/templates/components/ThreadChat/ThreadChat.tsx +862 -0
- package/epsimo/templates/components/ThreadChat/components/ToolRenderers.module.css +509 -0
- package/epsimo/templates/components/ThreadChat/components/ToolRenderers.tsx +322 -0
- package/epsimo/templates/next-mvp/app/globals.css.tmpl +20 -0
- package/epsimo/templates/next-mvp/app/layout.tsx.tmpl +22 -0
- package/epsimo/templates/next-mvp/app/page.module.css.tmpl +84 -0
- package/epsimo/templates/next-mvp/app/page.tsx.tmpl +43 -0
- package/epsimo/templates/next-mvp/epsimo.yaml.tmpl +12 -0
- package/epsimo/templates/next-mvp/package.json.tmpl +26 -0
- package/epsimo/tools/library.yaml +51 -0
- package/package.json +27 -0
- package/references/api_reference.md +34 -0
- package/references/virtual_db_guide.md +57 -0
- package/requirements.txt +2 -0
- package/scripts/assistant.py +165 -0
- package/scripts/auth.py +195 -0
- package/scripts/credits.py +107 -0
- package/scripts/debug_run.py +41 -0
- package/scripts/example.py +19 -0
- package/scripts/files.py +73 -0
- package/scripts/find_thread.py +55 -0
- package/scripts/project.py +60 -0
- package/scripts/run.py +75 -0
- package/scripts/test_all_skills.py +387 -0
- package/scripts/test_sdk.py +83 -0
- package/scripts/test_streaming.py +167 -0
- package/scripts/test_vdb.py +65 -0
- package/scripts/thread.py +77 -0
- package/scripts/verify_skill.py +87 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
import requests
|
|
6
|
+
import uuid
|
|
7
|
+
from auth import get_token, get_project_token, API_BASE_URL
|
|
8
|
+
|
|
9
|
+
# --- Colors for Output ---
|
|
10
|
+
class Colors:
|
|
11
|
+
HEADER = '\033[95m'
|
|
12
|
+
OKGREEN = '\033[92m'
|
|
13
|
+
OKCYAN = '\033[96m'
|
|
14
|
+
FAIL = '\033[91m'
|
|
15
|
+
ENDC = '\033[0m'
|
|
16
|
+
BOLD = '\033[1m'
|
|
17
|
+
|
|
18
|
+
def print_pass(msg):
|
|
19
|
+
print(f"{Colors.OKGREEN}ā
{msg}{Colors.ENDC}")
|
|
20
|
+
|
|
21
|
+
def print_fail(msg):
|
|
22
|
+
print(f"{Colors.FAIL}ā {msg}{Colors.ENDC}")
|
|
23
|
+
|
|
24
|
+
def print_info(msg):
|
|
25
|
+
print(f"{Colors.OKCYAN}ā¹ļø {msg}{Colors.ENDC}")
|
|
26
|
+
|
|
27
|
+
def run_streaming_test():
|
|
28
|
+
print(f"{Colors.HEADER}=== Starting Assistant Streaming Verification ==={Colors.ENDC}")
|
|
29
|
+
|
|
30
|
+
# 1. Auth
|
|
31
|
+
try:
|
|
32
|
+
token = get_token()
|
|
33
|
+
print_pass("Authenticated")
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print_fail(f"Auth failed: {e}")
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
# 2. Project
|
|
39
|
+
project_name = f"StreamTest {int(time.time())}"
|
|
40
|
+
print_info(f"Creating project '{project_name}'...")
|
|
41
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
resp = requests.post(f"{API_BASE_URL}/projects/", headers=headers, json={"name": project_name, "description": "Streaming test"})
|
|
45
|
+
resp.raise_for_status()
|
|
46
|
+
project_id = resp.json()["project_id"]
|
|
47
|
+
project_token = get_project_token(project_id)
|
|
48
|
+
print_pass(f"Project created: {project_id}")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print_fail(f"Project creation failed: {e}")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
# 3. Assistant
|
|
54
|
+
print_info("Creating simple assistant...")
|
|
55
|
+
p_headers = {"Authorization": f"Bearer {project_token}"}
|
|
56
|
+
|
|
57
|
+
asst_payload = {
|
|
58
|
+
"name": "Streaming Bot",
|
|
59
|
+
"config": {
|
|
60
|
+
"configurable": {
|
|
61
|
+
"type": "agent",
|
|
62
|
+
"type==agent/agent_type": "GPT-4O",
|
|
63
|
+
"type==agent/model": "gpt-4o",
|
|
64
|
+
"type==agent/system_message": "You are a helpful assistant. If asked to say something, say it exactly."
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"public": False
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
resp = requests.post(f"{API_BASE_URL}/assistants/", headers=p_headers, json=asst_payload)
|
|
72
|
+
resp.raise_for_status()
|
|
73
|
+
assistant_id = resp.json()["assistant_id"]
|
|
74
|
+
print_pass(f"Assistant created: {assistant_id}")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print_fail(f"Assistant creation failed: {e}")
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
# 4. Thread
|
|
80
|
+
print_info("Creating thread...")
|
|
81
|
+
try:
|
|
82
|
+
resp = requests.post(f"{API_BASE_URL}/threads/", headers=p_headers, json={"name": "Stream Thread", "assistant_id": assistant_id})
|
|
83
|
+
resp.raise_for_status()
|
|
84
|
+
thread_id = resp.json()["thread_id"]
|
|
85
|
+
print_pass(f"Thread created: {thread_id}")
|
|
86
|
+
except Exception as e:
|
|
87
|
+
print_fail(f"Thread creation failed: {e}")
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
# 5. Streaming Run
|
|
91
|
+
print_info("Ref: sending message 'Say: STREAMING_WORKS'...")
|
|
92
|
+
|
|
93
|
+
stream_headers = {
|
|
94
|
+
"Authorization": f"Bearer {project_token}",
|
|
95
|
+
"Accept": "text/event-stream"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
run_payload = {
|
|
99
|
+
"thread_id": thread_id,
|
|
100
|
+
"assistant_id": assistant_id,
|
|
101
|
+
"input": [{"role": "user", "content": "Say: STREAMING_WORKS", "type": "human"}],
|
|
102
|
+
"stream_mode": ["messages", "values"]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
full_text = ""
|
|
106
|
+
print(f"{Colors.OKCYAN}--- Stream Output Start ---{Colors.ENDC}")
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
with requests.post(f"{API_BASE_URL}/runs/stream", headers=stream_headers, json=run_payload, stream=True) as r:
|
|
110
|
+
r.raise_for_status()
|
|
111
|
+
for line in r.iter_lines():
|
|
112
|
+
if line:
|
|
113
|
+
decoded = line.decode('utf-8')
|
|
114
|
+
if decoded.startswith("data:"):
|
|
115
|
+
data_str = decoded[5:].strip()
|
|
116
|
+
if data_str == "[DONE]":
|
|
117
|
+
break
|
|
118
|
+
try:
|
|
119
|
+
# Attempt to parse json to find content (structure varies, so we print raw for debug if needed)
|
|
120
|
+
# Or just heuristic text accumulation
|
|
121
|
+
# Epsimo / LangGraph usually sends complex JSONs
|
|
122
|
+
data_json = json.loads(data_str)
|
|
123
|
+
|
|
124
|
+
# Heuristic extraction for various formats
|
|
125
|
+
content = ""
|
|
126
|
+
if isinstance(data_json, list): # Often a list of ops
|
|
127
|
+
for item in data_json:
|
|
128
|
+
if "content" in item:
|
|
129
|
+
content = item["content"]
|
|
130
|
+
elif isinstance(data_json, dict):
|
|
131
|
+
if "messages" in data_json:
|
|
132
|
+
# Extract last message content
|
|
133
|
+
pass
|
|
134
|
+
if "content" in data_json:
|
|
135
|
+
content = data_json["content"]
|
|
136
|
+
|
|
137
|
+
# Simple visual echo
|
|
138
|
+
if content:
|
|
139
|
+
sys.stdout.write(content)
|
|
140
|
+
sys.stdout.flush()
|
|
141
|
+
full_text += content
|
|
142
|
+
else:
|
|
143
|
+
# Fallback if we can't parse structure easily, just check raw for test
|
|
144
|
+
pass
|
|
145
|
+
except:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
print(f"\n{Colors.OKCYAN}--- Stream Output End ---{Colors.ENDC}")
|
|
149
|
+
|
|
150
|
+
if "STREAMING_WORKS" in full_text:
|
|
151
|
+
print_pass("Verification Successful: Received expected output!")
|
|
152
|
+
else:
|
|
153
|
+
print_fail("Did not receive exact expected phrase, but stream finished.")
|
|
154
|
+
print_info(f"Received total length: {len(full_text)}")
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
print_fail(f"Streaming failed: {e}")
|
|
158
|
+
|
|
159
|
+
# 6. Cleanup
|
|
160
|
+
try:
|
|
161
|
+
requests.delete(f"{API_BASE_URL}/projects/{project_id}?confirm=true", headers=headers)
|
|
162
|
+
print_pass("Cleanup: Project deleted")
|
|
163
|
+
except:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
run_streaming_test()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
# Add parent dir to path to find epsimo package
|
|
6
|
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
7
|
+
|
|
8
|
+
from epsimo import EpsimoClient
|
|
9
|
+
from epsimo.auth import get_token
|
|
10
|
+
|
|
11
|
+
def test_vdb():
|
|
12
|
+
print("š§Ŗ Testing Virtual Database via Threads...")
|
|
13
|
+
|
|
14
|
+
# Needs a real project and thread from the user's environment
|
|
15
|
+
PROJECT_ID = "cb323eb1-768e-4702-b7af-c73a1d6ce0e1"
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
token = get_token()
|
|
19
|
+
client = EpsimoClient(api_key=token)
|
|
20
|
+
|
|
21
|
+
# 1. Create a fresh thread for DB testing
|
|
22
|
+
print("Creating fresh DB thread...")
|
|
23
|
+
# Get an assistant first
|
|
24
|
+
assistants = client.assistants.list(PROJECT_ID)
|
|
25
|
+
if not assistants:
|
|
26
|
+
print("ā No assistants found in project.")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
asst_id = assistants[0]["assistant_id"]
|
|
30
|
+
thread = client.threads.create(PROJECT_ID, "Virtual DB Test", asst_id)
|
|
31
|
+
thread_id = thread["thread_id"]
|
|
32
|
+
print(f"ā
Thread Created: {thread_id}")
|
|
33
|
+
|
|
34
|
+
# 2. Set some structured data
|
|
35
|
+
print("Setting data in DB...")
|
|
36
|
+
db_data = {
|
|
37
|
+
"user_settings": {
|
|
38
|
+
"theme": "dark",
|
|
39
|
+
"notifications": True
|
|
40
|
+
},
|
|
41
|
+
"last_login": "2026-02-03",
|
|
42
|
+
"active_session": True
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
client.threads.set_state(PROJECT_ID, thread_id, db_data)
|
|
46
|
+
print("ā
Data persisted.")
|
|
47
|
+
|
|
48
|
+
# 3. Retrieve and verify
|
|
49
|
+
print("Querying DB...")
|
|
50
|
+
state = client.threads.get_state(PROJECT_ID, thread_id)
|
|
51
|
+
values = state.get("values", {})
|
|
52
|
+
|
|
53
|
+
print("\n=== Virtual DB Content ===")
|
|
54
|
+
print(json.dumps(values, indent=2))
|
|
55
|
+
|
|
56
|
+
if values.get("user_settings", {}).get("theme") == "dark":
|
|
57
|
+
print("\nš SUCCESS: Virtual Database is working correctly!")
|
|
58
|
+
else:
|
|
59
|
+
print("\nā FAILURE: Data mismatch or not found.")
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print(f"ā Test Failed: {e}")
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
test_vdb()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
import requests
|
|
6
|
+
import json
|
|
7
|
+
from auth import get_token, get_project_token, API_BASE_URL
|
|
8
|
+
|
|
9
|
+
def create_thread(project_id, name, assistant_id=None):
|
|
10
|
+
token = get_token()
|
|
11
|
+
|
|
12
|
+
# Get project specific token
|
|
13
|
+
project_token = get_project_token(project_id)
|
|
14
|
+
|
|
15
|
+
headers = {"Authorization": f"Bearer {project_token}"}
|
|
16
|
+
|
|
17
|
+
# Metadata structure from frontend
|
|
18
|
+
payload = {
|
|
19
|
+
"name": name,
|
|
20
|
+
"metadata": {
|
|
21
|
+
"configurable": {},
|
|
22
|
+
"type": "thread"
|
|
23
|
+
},
|
|
24
|
+
"configurable": {
|
|
25
|
+
"type": "agent"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if assistant_id:
|
|
29
|
+
payload["assistant_id"] = assistant_id
|
|
30
|
+
|
|
31
|
+
response = requests.post(f"{API_BASE_URL}/threads/", headers=headers, json=payload)
|
|
32
|
+
|
|
33
|
+
if not response.ok:
|
|
34
|
+
print(f"Status: {response.status_code}")
|
|
35
|
+
print(f"Body: {response.text}")
|
|
36
|
+
|
|
37
|
+
response.raise_for_status()
|
|
38
|
+
print(json.dumps(response.json(), indent=2))
|
|
39
|
+
|
|
40
|
+
def list_threads(project_id):
|
|
41
|
+
token = get_token()
|
|
42
|
+
|
|
43
|
+
# Get project specific token
|
|
44
|
+
auth_headers = {"Authorization": f"Bearer {token}"}
|
|
45
|
+
proj_resp = requests.get(f"{API_BASE_URL}/projects/{project_id}", headers=auth_headers)
|
|
46
|
+
proj_resp.raise_for_status()
|
|
47
|
+
data = proj_resp.json()
|
|
48
|
+
project_token = data.get('access_token') or data.get('token') or data.get('jwt_token')
|
|
49
|
+
|
|
50
|
+
headers = {"Authorization": f"Bearer {project_token}"}
|
|
51
|
+
response = requests.get(f"{API_BASE_URL}/threads/", headers=headers)
|
|
52
|
+
response.raise_for_status()
|
|
53
|
+
print(json.dumps(response.json(), indent=2))
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
parser = argparse.ArgumentParser(description="Manage EpsimoAI Threads")
|
|
57
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
58
|
+
|
|
59
|
+
# List
|
|
60
|
+
list_parser = subparsers.add_parser("list", help="List threads in a project")
|
|
61
|
+
list_parser.add_argument("--project-id", required=True, help="Project ID")
|
|
62
|
+
|
|
63
|
+
# Create
|
|
64
|
+
create_parser = subparsers.add_parser("create", help="Create a new thread")
|
|
65
|
+
create_parser.add_argument("--project-id", required=True, help="Project ID")
|
|
66
|
+
create_parser.add_argument("--name", default="New Thread", help="Thread name")
|
|
67
|
+
create_parser.add_argument("--assistant-id", help="Assistant ID to associate with the thread (optional)")
|
|
68
|
+
|
|
69
|
+
args = parser.parse_args()
|
|
70
|
+
|
|
71
|
+
if args.command == "list":
|
|
72
|
+
list_threads(args.project_id)
|
|
73
|
+
elif args.command == "create":
|
|
74
|
+
create_thread(args.project_id, args.name, args.assistant_id)
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
main()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
|
|
9
|
+
def run_command(cmd, capture_output=True):
|
|
10
|
+
print(f"Running: {cmd}")
|
|
11
|
+
# Add PYTHONPATH to find epsimo package
|
|
12
|
+
env = os.environ.copy()
|
|
13
|
+
env["PYTHONPATH"] = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
14
|
+
|
|
15
|
+
result = subprocess.run(cmd, shell=True, capture_output=capture_output, text=True, env=env)
|
|
16
|
+
if result.returncode != 0:
|
|
17
|
+
print(f"Error: {result.stderr}")
|
|
18
|
+
raise Exception(f"Command failed: {cmd}")
|
|
19
|
+
return result.stdout.strip()
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
print("š Starting Epsimo Agent Skill Verification (CLI Version)...")
|
|
23
|
+
|
|
24
|
+
# 1. Auth Check - we do this in the root to ensure we have credentials
|
|
25
|
+
print("\n1ļøā£ Checking Authentication...")
|
|
26
|
+
try:
|
|
27
|
+
run_command("python3 -m epsimo.cli whoami")
|
|
28
|
+
except:
|
|
29
|
+
print("š Authentication required. Please run 'epsimo auth login' first.")
|
|
30
|
+
sys.exit(1)
|
|
31
|
+
|
|
32
|
+
# Setup temp workspace
|
|
33
|
+
temp_workspace = f"verify-workspace-{int(time.time())}"
|
|
34
|
+
os.makedirs(temp_workspace)
|
|
35
|
+
orig_dir = os.getcwd()
|
|
36
|
+
os.chdir(temp_workspace)
|
|
37
|
+
print(f"š Created temporary workspace: {temp_workspace}")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
# 2. Project Scaffolding
|
|
41
|
+
print("\n2ļøā£ Verifying Project Scaffolding...")
|
|
42
|
+
test_slug = "verify-app"
|
|
43
|
+
run_command(f"python3 -m epsimo.cli create 'Verify App'")
|
|
44
|
+
if os.path.exists(test_slug) and os.path.exists(f"{test_slug}/epsimo.yaml"):
|
|
45
|
+
print("ā
Scaffolding SUCCESS: App structure created.")
|
|
46
|
+
else:
|
|
47
|
+
raise Exception("App structure missing files.")
|
|
48
|
+
|
|
49
|
+
# 3. End-to-End lifecycle
|
|
50
|
+
print("\n3ļøā£ Verifying Project/Assistant Lifecycle...")
|
|
51
|
+
proj_name = f"VerifyProj-{int(time.time())}"
|
|
52
|
+
run_command(f"python3 -m epsimo.cli init --name '{proj_name}'")
|
|
53
|
+
|
|
54
|
+
with open("epsimo.yaml", "r") as f:
|
|
55
|
+
import yaml
|
|
56
|
+
cfg = yaml.safe_load(f)
|
|
57
|
+
project_id = cfg['project_id']
|
|
58
|
+
|
|
59
|
+
print(f"ā
Created Project: {project_id}")
|
|
60
|
+
|
|
61
|
+
# Deploy (creates assistant)
|
|
62
|
+
run_command("python3 -m epsimo.cli deploy")
|
|
63
|
+
print("ā
Deployed Assistant.")
|
|
64
|
+
|
|
65
|
+
# Discovery Check
|
|
66
|
+
asst_json = run_command(f"python3 -m epsimo.cli assistants --project-id {project_id} --json")
|
|
67
|
+
assistants = json.loads(asst_json)
|
|
68
|
+
if not assistants:
|
|
69
|
+
raise Exception("No assistants found after deploy.")
|
|
70
|
+
|
|
71
|
+
print(f"ā
Discovery SUCCESS: Found {len(assistants)} assistants.")
|
|
72
|
+
|
|
73
|
+
# 4. Logic responsive check
|
|
74
|
+
print("\n4ļøā£ Checking Credits...")
|
|
75
|
+
run_command("python3 -m epsimo.cli credits balance")
|
|
76
|
+
print("ā
Logic Check: Responsive.")
|
|
77
|
+
|
|
78
|
+
finally:
|
|
79
|
+
os.chdir(orig_dir)
|
|
80
|
+
if os.path.exists(temp_workspace):
|
|
81
|
+
shutil.rmtree(temp_workspace)
|
|
82
|
+
|
|
83
|
+
print("\nš Skill Verification Completed Successfully!")
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
main()
|
|
87
|
+
|