solana-agent 8.0.2__py3-none-any.whl → 8.1.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.
- solana_agent/ai.py +237 -12
- {solana_agent-8.0.2.dist-info → solana_agent-8.1.0.dist-info}/METADATA +17 -11
- solana_agent-8.1.0.dist-info/RECORD +6 -0
- solana_agent-8.0.2.dist-info/RECORD +0 -6
- {solana_agent-8.0.2.dist-info → solana_agent-8.1.0.dist-info}/LICENSE +0 -0
- {solana_agent-8.0.2.dist-info → solana_agent-8.1.0.dist-info}/WHEEL +0 -0
solana_agent/ai.py
CHANGED
@@ -21,7 +21,25 @@ from zep_cloud.types import Message
|
|
21
21
|
from pinecone import Pinecone
|
22
22
|
|
23
23
|
|
24
|
+
# Add this to the top of the file with other Pydantic models
|
25
|
+
class TicketResolution(BaseModel):
|
26
|
+
status: Literal["resolved", "needs_followup", "cannot_determine"] = Field(
|
27
|
+
..., description="Resolution status of the ticket"
|
28
|
+
)
|
29
|
+
confidence: float = Field(
|
30
|
+
..., description="Confidence score for the resolution decision (0.0-1.0)"
|
31
|
+
)
|
32
|
+
reasoning: str = Field(
|
33
|
+
..., description="Brief explanation for the resolution decision"
|
34
|
+
)
|
35
|
+
suggested_actions: List[str] = Field(
|
36
|
+
default_factory=list, description="Suggested follow-up actions if needed"
|
37
|
+
)
|
38
|
+
|
39
|
+
|
24
40
|
# Define Pydantic models for structured output
|
41
|
+
|
42
|
+
|
25
43
|
class ImprovementArea(BaseModel):
|
26
44
|
area: str = Field(...,
|
27
45
|
description="Area name (e.g., 'Accuracy', 'Completeness')")
|
@@ -2278,7 +2296,7 @@ class Swarm:
|
|
2278
2296
|
async def process(
|
2279
2297
|
self, user_id: str, user_text: str, timezone: str = None
|
2280
2298
|
) -> AsyncGenerator[str, None]:
|
2281
|
-
"""Process the user request with appropriate agent and handle
|
2299
|
+
"""Process the user request with appropriate agent and handle ticket management.
|
2282
2300
|
|
2283
2301
|
Args:
|
2284
2302
|
user_id (str): Unique user identifier
|
@@ -2291,7 +2309,7 @@ class Swarm:
|
|
2291
2309
|
yield await self._process_ticket_commands(user_id, user_text)
|
2292
2310
|
return
|
2293
2311
|
|
2294
|
-
# Handle special commands
|
2312
|
+
# Handle special collective memory commands
|
2295
2313
|
if user_text.strip().lower().startswith("!memory "):
|
2296
2314
|
query = user_text[8:].strip()
|
2297
2315
|
yield self.search_collective_memory(query)
|
@@ -2302,11 +2320,96 @@ class Swarm:
|
|
2302
2320
|
yield "Error: No agents are registered with the system. Please register at least one agent first."
|
2303
2321
|
return
|
2304
2322
|
|
2305
|
-
#
|
2306
|
-
|
2307
|
-
|
2308
|
-
|
2309
|
-
|
2323
|
+
# Ensure tickets collection exists
|
2324
|
+
if "tickets" not in self.database.db.list_collection_names():
|
2325
|
+
self.database.db.create_collection("tickets")
|
2326
|
+
self.tickets = self.database.db["tickets"]
|
2327
|
+
|
2328
|
+
# Check if this is continuing an existing ticket
|
2329
|
+
active_ticket = self.tickets.find_one(
|
2330
|
+
{
|
2331
|
+
"user_id": user_id,
|
2332
|
+
"status": {"$in": ["pending", "active", "transferred"]},
|
2333
|
+
}
|
2334
|
+
)
|
2335
|
+
|
2336
|
+
ticket_id = None
|
2337
|
+
current_agent = None
|
2338
|
+
|
2339
|
+
if active_ticket:
|
2340
|
+
# Continue with existing ticket
|
2341
|
+
ticket_id = active_ticket["_id"]
|
2342
|
+
current_agent_name = active_ticket.get("assigned_to")
|
2343
|
+
|
2344
|
+
# Update ticket to active status if it was pending/transferred
|
2345
|
+
if active_ticket["status"] in ["pending", "transferred"]:
|
2346
|
+
self.tickets.update_one(
|
2347
|
+
{"_id": ticket_id},
|
2348
|
+
{
|
2349
|
+
"$set": {
|
2350
|
+
"status": "active",
|
2351
|
+
"last_activity": datetime.datetime.now(
|
2352
|
+
datetime.timezone.utc
|
2353
|
+
),
|
2354
|
+
}
|
2355
|
+
},
|
2356
|
+
)
|
2357
|
+
|
2358
|
+
# If it was transferred to a specific agent, use that agent
|
2359
|
+
if current_agent_name and current_agent_name in self.agents:
|
2360
|
+
current_agent = self.agents[current_agent_name]
|
2361
|
+
print(
|
2362
|
+
f"Continuing ticket {ticket_id} with agent {current_agent_name}"
|
2363
|
+
)
|
2364
|
+
else:
|
2365
|
+
# Get routing decision if no specific agent is assigned
|
2366
|
+
first_agent = next(iter(self.agents.values()))
|
2367
|
+
agent_name = await self._get_routing_decision(
|
2368
|
+
first_agent, user_text
|
2369
|
+
)
|
2370
|
+
current_agent = self.agents[agent_name]
|
2371
|
+
|
2372
|
+
# Update ticket with selected agent
|
2373
|
+
self.tickets.update_one(
|
2374
|
+
{"_id": ticket_id},
|
2375
|
+
{
|
2376
|
+
"$set": {
|
2377
|
+
"assigned_to": agent_name,
|
2378
|
+
"updated_at": datetime.datetime.now(
|
2379
|
+
datetime.timezone.utc
|
2380
|
+
),
|
2381
|
+
}
|
2382
|
+
},
|
2383
|
+
)
|
2384
|
+
print(f"Reassigned ticket {ticket_id} to {agent_name}")
|
2385
|
+
else:
|
2386
|
+
# Create new ticket for this interaction
|
2387
|
+
ticket_id = str(uuid.uuid4())
|
2388
|
+
|
2389
|
+
# Get initial routing and agent
|
2390
|
+
first_agent = next(iter(self.agents.values()))
|
2391
|
+
agent_name = await self._get_routing_decision(first_agent, user_text)
|
2392
|
+
current_agent = self.agents[agent_name]
|
2393
|
+
|
2394
|
+
# Get conversation context
|
2395
|
+
context = ""
|
2396
|
+
if hasattr(current_agent, "get_memory_context"):
|
2397
|
+
context = current_agent.get_memory_context(user_id)
|
2398
|
+
|
2399
|
+
# Store new ticket in database
|
2400
|
+
self.tickets.insert_one(
|
2401
|
+
{
|
2402
|
+
"_id": ticket_id,
|
2403
|
+
"user_id": user_id,
|
2404
|
+
"query": user_text,
|
2405
|
+
"created_at": datetime.datetime.now(datetime.timezone.utc),
|
2406
|
+
"assigned_to": agent_name,
|
2407
|
+
"status": "active",
|
2408
|
+
"context": context,
|
2409
|
+
}
|
2410
|
+
)
|
2411
|
+
print(
|
2412
|
+
f"Created new ticket {ticket_id}, assigned to {agent_name}")
|
2310
2413
|
|
2311
2414
|
# Reset handoff info
|
2312
2415
|
current_agent._handoff_info = None
|
@@ -2314,18 +2417,64 @@ class Swarm:
|
|
2314
2417
|
# Response tracking
|
2315
2418
|
final_response = ""
|
2316
2419
|
|
2317
|
-
# Process response stream
|
2420
|
+
# Process response stream with ticket context
|
2318
2421
|
async for chunk in self._stream_response(
|
2319
|
-
user_id, user_text, current_agent, timezone
|
2422
|
+
user_id, user_text, current_agent, timezone, ticket_id
|
2320
2423
|
):
|
2321
2424
|
yield chunk
|
2322
2425
|
final_response += chunk
|
2323
2426
|
|
2427
|
+
# Skip ticket resolution check if a handoff occurred during response
|
2428
|
+
if not current_agent._handoff_info:
|
2429
|
+
# Check if ticket should be resolved based on AI's response
|
2430
|
+
resolution = await self._check_ticket_resolution(
|
2431
|
+
user_id, final_response, ticket_id
|
2432
|
+
)
|
2433
|
+
|
2434
|
+
# Update ticket status based on resolution
|
2435
|
+
if resolution.status == "resolved" and resolution.confidence >= 0.7:
|
2436
|
+
self.tickets.update_one(
|
2437
|
+
{"_id": ticket_id},
|
2438
|
+
{
|
2439
|
+
"$set": {
|
2440
|
+
"status": "resolved",
|
2441
|
+
"resolution_confidence": resolution.confidence,
|
2442
|
+
"resolution_reasoning": resolution.reasoning,
|
2443
|
+
"resolved_at": datetime.datetime.now(
|
2444
|
+
datetime.timezone.utc
|
2445
|
+
),
|
2446
|
+
}
|
2447
|
+
},
|
2448
|
+
)
|
2449
|
+
print(
|
2450
|
+
f"Ticket {ticket_id} marked as resolved with confidence {resolution.confidence}"
|
2451
|
+
)
|
2452
|
+
else:
|
2453
|
+
# Update with pending status
|
2454
|
+
self.tickets.update_one(
|
2455
|
+
{"_id": ticket_id},
|
2456
|
+
{
|
2457
|
+
"$set": {
|
2458
|
+
"status": "pending_confirmation",
|
2459
|
+
"resolution_confidence": resolution.confidence,
|
2460
|
+
"resolution_reasoning": resolution.reasoning,
|
2461
|
+
"suggested_actions": resolution.suggested_actions,
|
2462
|
+
"updated_at": datetime.datetime.now(
|
2463
|
+
datetime.timezone.utc
|
2464
|
+
),
|
2465
|
+
}
|
2466
|
+
},
|
2467
|
+
)
|
2468
|
+
print(
|
2469
|
+
f"Ticket {ticket_id} needs followup (confidence: {resolution.confidence})"
|
2470
|
+
)
|
2471
|
+
|
2324
2472
|
# Post-processing: learn from conversation
|
2325
2473
|
conversation = {
|
2326
2474
|
"user_id": user_id,
|
2327
2475
|
"message": user_text,
|
2328
2476
|
"response": final_response,
|
2477
|
+
"ticket_id": ticket_id,
|
2329
2478
|
}
|
2330
2479
|
|
2331
2480
|
# Run post-processing tasks concurrently
|
@@ -2336,9 +2485,8 @@ class Swarm:
|
|
2336
2485
|
tasks.append(self.extract_and_store_insights(
|
2337
2486
|
user_id, conversation))
|
2338
2487
|
|
2339
|
-
# Run all post-processing tasks concurrently
|
2488
|
+
# Run all post-processing tasks concurrently without waiting
|
2340
2489
|
if tasks:
|
2341
|
-
# Don't block - run asynchronously
|
2342
2490
|
asyncio.create_task(self._run_post_processing_tasks(tasks))
|
2343
2491
|
|
2344
2492
|
except Exception as e:
|
@@ -2346,6 +2494,64 @@ class Swarm:
|
|
2346
2494
|
print(traceback.format_exc())
|
2347
2495
|
yield "\n\nI apologize for the technical difficulty.\n\n"
|
2348
2496
|
|
2497
|
+
async def _check_ticket_resolution(self, user_id, response, ticket_id):
|
2498
|
+
"""Determine if a ticket can be resolved based on the AI response using structured output.
|
2499
|
+
|
2500
|
+
Args:
|
2501
|
+
user_id: The user identifier
|
2502
|
+
response: The AI agent's response to evaluate
|
2503
|
+
ticket_id: The ticket identifier
|
2504
|
+
|
2505
|
+
Returns:
|
2506
|
+
TicketResolution: Structured resolution data with status, confidence and reasoning
|
2507
|
+
"""
|
2508
|
+
# Get first agent to use its client
|
2509
|
+
first_agent = next(iter(self.agents.values()))
|
2510
|
+
|
2511
|
+
# Get ticket details
|
2512
|
+
ticket = self.tickets.find_one({"_id": ticket_id})
|
2513
|
+
if not ticket:
|
2514
|
+
return TicketResolution(
|
2515
|
+
status="cannot_determine",
|
2516
|
+
confidence=0.0,
|
2517
|
+
reasoning="Ticket not found in database",
|
2518
|
+
)
|
2519
|
+
|
2520
|
+
prompt = f"""
|
2521
|
+
You are evaluating if a user's question has been fully addressed.
|
2522
|
+
|
2523
|
+
Original query: {ticket.get('query', 'Unknown')}
|
2524
|
+
|
2525
|
+
Agent response: {response}
|
2526
|
+
|
2527
|
+
Analyze how well the response addresses the query and provide a structured assessment.
|
2528
|
+
"""
|
2529
|
+
|
2530
|
+
try:
|
2531
|
+
# Use structured parsing to get detailed resolution information
|
2532
|
+
resolution_response = first_agent._client.beta.chat.completions.parse(
|
2533
|
+
model=self.router_model,
|
2534
|
+
messages=[
|
2535
|
+
{"role": "system", "content": prompt},
|
2536
|
+
{
|
2537
|
+
"role": "user",
|
2538
|
+
"content": "Provide a structured assessment of this response.",
|
2539
|
+
},
|
2540
|
+
],
|
2541
|
+
response_format=TicketResolution,
|
2542
|
+
temperature=0.1,
|
2543
|
+
)
|
2544
|
+
|
2545
|
+
return resolution_response.choices[0].message.parsed
|
2546
|
+
|
2547
|
+
except Exception as e:
|
2548
|
+
print(f"Error checking ticket resolution: {e}")
|
2549
|
+
return TicketResolution(
|
2550
|
+
status="cannot_determine",
|
2551
|
+
confidence=0.0,
|
2552
|
+
reasoning=f"Error processing resolution check: {str(e)}",
|
2553
|
+
)
|
2554
|
+
|
2349
2555
|
async def _process_ticket_commands(self, user_id: str, command: str) -> str:
|
2350
2556
|
"""Process ticket management commands directly in chat."""
|
2351
2557
|
parts = command.strip().split(" ", 2)
|
@@ -2593,7 +2799,7 @@ class Swarm:
|
|
2593
2799
|
return "just now"
|
2594
2800
|
|
2595
2801
|
async def _stream_response(
|
2596
|
-
self, user_id, user_text, current_agent, timezone=None
|
2802
|
+
self, user_id, user_text, current_agent, timezone=None, ticket_id=None
|
2597
2803
|
) -> AsyncGenerator[str, None]:
|
2598
2804
|
"""Stream response from an agent, handling potential handoffs to AI or human agents."""
|
2599
2805
|
handoff_detected = False
|
@@ -2751,6 +2957,25 @@ class Swarm:
|
|
2751
2957
|
# Standard AI-to-AI handoff
|
2752
2958
|
target_agent = self.agents[target_name]
|
2753
2959
|
|
2960
|
+
# Update the ticket if we have one
|
2961
|
+
if ticket_id:
|
2962
|
+
self.tickets.update_one(
|
2963
|
+
{"_id": ticket_id},
|
2964
|
+
{
|
2965
|
+
"$set": {
|
2966
|
+
"assigned_to": target_name,
|
2967
|
+
"status": "transferred",
|
2968
|
+
"handoff_reason": reason,
|
2969
|
+
"updated_at": datetime.datetime.now(
|
2970
|
+
datetime.timezone.utc
|
2971
|
+
),
|
2972
|
+
}
|
2973
|
+
},
|
2974
|
+
)
|
2975
|
+
print(
|
2976
|
+
f"Updated ticket {ticket_id}, transferred to {target_name}"
|
2977
|
+
)
|
2978
|
+
|
2754
2979
|
# Pass to target agent with comprehensive instructions
|
2755
2980
|
handoff_query = f"""
|
2756
2981
|
Answer this ENTIRE question completely from scratch:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: solana-agent
|
3
|
-
Version: 8.0
|
4
|
-
Summary: The
|
3
|
+
Version: 8.1.0
|
4
|
+
Summary: The Future of Work
|
5
5
|
License: MIT
|
6
6
|
Keywords: ai,openai,ai agents,agi
|
7
7
|
Author: Bevan Hunt
|
@@ -131,21 +131,19 @@ Eliminate management hierarchies while maintaining coordination and alignment:
|
|
131
131
|
Solana Agent transforms organizations into living systems that continuously learn, adapt, and evolve:
|
132
132
|
|
133
133
|
- **Collective Memory Acceleration:**
|
134
|
-
|
134
|
+
Extract and preserve organizational knowledge across all agents automatically.
|
135
135
|
- **Autonomous Self-Improvement:**
|
136
|
-
|
136
|
+
AI critic framework identifies weaknesses and implements improvements without human intervention.
|
137
137
|
- **Experiential Learning Loops:**
|
138
|
-
Every interaction
|
138
|
+
Every interaction refines capabilities through rapid feedback cycles.
|
139
139
|
- **Cross-Domain Knowledge Synthesis:**
|
140
|
-
Break down
|
140
|
+
Break down information silos through semantic connections across domains.
|
141
141
|
- **Living Knowledge Repository:**
|
142
|
-
Build
|
142
|
+
Build an organizational brain that becomes more valuable with each interaction.
|
143
143
|
- **Pattern Recognition at Scale:**
|
144
|
-
Identify
|
145
|
-
- **Insight-Driven Evolution:**
|
146
|
-
Let the organization adapt its own structure and processes based on empirically-identified patterns of effectiveness.
|
144
|
+
Identify trends invisible to individual actors through network-wide analysis.
|
147
145
|
- **Failure-Embracing Architecture:**
|
148
|
-
|
146
|
+
Convert mistakes into learning opportunities through systematic analysis.
|
149
147
|
|
150
148
|
## Technical Features
|
151
149
|
|
@@ -174,6 +172,14 @@ Solana Agent transforms organizations into living systems that continuously lear
|
|
174
172
|
Time-aware contextual responses.
|
175
173
|
Self-critical improvement systems.
|
176
174
|
|
175
|
+
- **🎫 Comprehensive Ticket System:**
|
176
|
+
Universal ticket tracking for all interactions (AI-to-AI and AI-to-human).
|
177
|
+
Structured resolution assessment with confidence scoring.
|
178
|
+
Automatic ticket lifecycle management with AI-driven resolution.
|
179
|
+
Intelligent handoffs maintaining ticket context across agents.
|
180
|
+
Command-line interface for human ticket management.
|
181
|
+
Metrics and analytics on resolution quality and time.
|
182
|
+
|
177
183
|
- **🛡️ Governance Framework:**
|
178
184
|
Define organization-wide values and operating principles in code.
|
179
185
|
Consistent decision-making aligned with organizational priorities.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
solana_agent/__init__.py,sha256=zpfnWqANd3OHGWm7NCF5Y6m01BWG4NkNk8SK9Ex48nA,18
|
2
|
+
solana_agent/ai.py,sha256=wgNWzJU0AJf8ZL71D-phXau7jz7DIhJjSErEQ39_QyE,129937
|
3
|
+
solana_agent-8.1.0.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
|
4
|
+
solana_agent-8.1.0.dist-info/METADATA,sha256=QrXQirdpnwlihF4CUqvESiAQIVkcrKqRkEUqJm6VFX0,11701
|
5
|
+
solana_agent-8.1.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
6
|
+
solana_agent-8.1.0.dist-info/RECORD,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
solana_agent/__init__.py,sha256=zpfnWqANd3OHGWm7NCF5Y6m01BWG4NkNk8SK9Ex48nA,18
|
2
|
-
solana_agent/ai.py,sha256=V-FnRpeelDXWFXtNo-nh2vJmokW-rh8YmMP0-ws5-AE,120530
|
3
|
-
solana_agent-8.0.2.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
|
4
|
-
solana_agent-8.0.2.dist-info/METADATA,sha256=370AP4B-rDvVj0RLyXpHu45Wc_FZ5SWunvC4GXouYbs,11686
|
5
|
-
solana_agent-8.0.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
6
|
-
solana_agent-8.0.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|