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 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 handoffs.
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
- # Get initial routing and agent
2306
- first_agent = next(iter(self.agents.values()))
2307
- agent_name = await self._get_routing_decision(first_agent, user_text)
2308
- current_agent = self.agents[agent_name]
2309
- print(f"Starting conversation with agent: {agent_name}")
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.2
4
- Summary: The first AGI framework
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
- Prevent organizational knowledge loss by automatically extracting, preserving, and distributing insights across all agents.
134
+ Extract and preserve organizational knowledge across all agents automatically.
135
135
  - **Autonomous Self-Improvement:**
136
- The system identifies its own weaknesses through the critic framework and actively improves without human intervention.
136
+ AI critic framework identifies weaknesses and implements improvements without human intervention.
137
137
  - **Experiential Learning Loops:**
138
- Every interaction becomes training data, creating rapid feedback loops that continuously refine agent capabilities.
138
+ Every interaction refines capabilities through rapid feedback cycles.
139
139
  - **Cross-Domain Knowledge Synthesis:**
140
- Break down knowledge silos by connecting insights across traditionally separate domains through semantic understanding.
140
+ Break down information silos through semantic connections across domains.
141
141
  - **Living Knowledge Repository:**
142
- Build a dynamic, ever-evolving organizational brain that grows more valuable and accurate with each interaction.
142
+ Build an organizational brain that becomes more valuable with each interaction.
143
143
  - **Pattern Recognition at Scale:**
144
- Identify emerging trends and insights invisible to individual actors by analyzing connections across all interactions.
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
- Turn mistakes into accelerated learning opportunities through systematic analysis and continuous adaptation.
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,,