solana-agent 11.1.0__py3-none-any.whl → 11.2.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 +272 -147
- {solana_agent-11.1.0.dist-info → solana_agent-11.2.0.dist-info}/METADATA +2 -3
- solana_agent-11.2.0.dist-info/RECORD +6 -0
- solana_agent-11.1.0.dist-info/RECORD +0 -6
- {solana_agent-11.1.0.dist-info → solana_agent-11.2.0.dist-info}/LICENSE +0 -0
- {solana_agent-11.1.0.dist-info → solana_agent-11.2.0.dist-info}/WHEEL +0 -0
solana_agent/ai.py
CHANGED
@@ -15,6 +15,7 @@ import datetime
|
|
15
15
|
import json
|
16
16
|
import re
|
17
17
|
import traceback
|
18
|
+
from unittest.mock import AsyncMock
|
18
19
|
import uuid
|
19
20
|
from enum import Enum
|
20
21
|
from typing import (
|
@@ -1355,6 +1356,15 @@ class MongoTicketRepository:
|
|
1355
1356
|
"""Count tickets matching query."""
|
1356
1357
|
return self.db.count_documents(self.collection, query)
|
1357
1358
|
|
1359
|
+
def find_stalled_tickets(self, cutoff_time, statuses):
|
1360
|
+
"""Find tickets that haven't been updated since the cutoff time."""
|
1361
|
+
query = {
|
1362
|
+
"status": {"$in": [status.value if isinstance(status, Enum) else status for status in statuses]},
|
1363
|
+
"updated_at": {"$lt": cutoff_time}
|
1364
|
+
}
|
1365
|
+
tickets = self.db_adapter.find("tickets", query)
|
1366
|
+
return [Ticket(**ticket) for ticket in tickets]
|
1367
|
+
|
1358
1368
|
|
1359
1369
|
class MongoHandoffRepository:
|
1360
1370
|
"""MongoDB implementation of HandoffRepository."""
|
@@ -3018,27 +3028,6 @@ class ProjectApprovalService:
|
|
3018
3028
|
if agent_id in self.human_agent_registry.get_all_human_agents():
|
3019
3029
|
self.approvers.append(agent_id)
|
3020
3030
|
|
3021
|
-
async def submit_for_approval(self, ticket: Ticket) -> None:
|
3022
|
-
"""Submit a project for human approval."""
|
3023
|
-
# Update ticket status
|
3024
|
-
self.ticket_repository.update(
|
3025
|
-
ticket.id,
|
3026
|
-
{
|
3027
|
-
"status": TicketStatus.PENDING,
|
3028
|
-
"approval_status": "awaiting_approval",
|
3029
|
-
"updated_at": datetime.datetime.now(datetime.timezone.utc),
|
3030
|
-
},
|
3031
|
-
)
|
3032
|
-
|
3033
|
-
# Notify approvers
|
3034
|
-
if self.notification_service:
|
3035
|
-
for approver_id in self.approvers:
|
3036
|
-
self.notification_service.send_notification(
|
3037
|
-
approver_id,
|
3038
|
-
f"New project requires approval: {ticket.query}",
|
3039
|
-
{"ticket_id": ticket.id, "type": "approval_request"},
|
3040
|
-
)
|
3041
|
-
|
3042
3031
|
async def process_approval(
|
3043
3032
|
self, ticket_id: str, approver_id: str, approved: bool, comments: str = ""
|
3044
3033
|
) -> None:
|
@@ -3073,6 +3062,27 @@ class ProjectApprovalService:
|
|
3073
3062
|
},
|
3074
3063
|
)
|
3075
3064
|
|
3065
|
+
async def submit_for_approval(self, ticket: Ticket) -> None:
|
3066
|
+
"""Submit a project for human approval."""
|
3067
|
+
# Update ticket status
|
3068
|
+
self.ticket_repository.update(
|
3069
|
+
ticket.id,
|
3070
|
+
{
|
3071
|
+
"status": TicketStatus.PENDING,
|
3072
|
+
"approval_status": "awaiting_approval",
|
3073
|
+
"updated_at": datetime.datetime.now(datetime.timezone.utc),
|
3074
|
+
},
|
3075
|
+
)
|
3076
|
+
|
3077
|
+
# Notify approvers
|
3078
|
+
if self.notification_service and self.approvers:
|
3079
|
+
for approver_id in self.approvers:
|
3080
|
+
await self.notification_service.send_notification(
|
3081
|
+
approver_id,
|
3082
|
+
f"New project requires approval: {ticket.query}",
|
3083
|
+
{"ticket_id": ticket.id, "type": "approval_request"},
|
3084
|
+
)
|
3085
|
+
|
3076
3086
|
|
3077
3087
|
class ProjectSimulationService:
|
3078
3088
|
"""Service for simulating project feasibility and requirements using historical data."""
|
@@ -4446,6 +4456,7 @@ class QueryProcessor:
|
|
4446
4456
|
project_simulation_service: Optional[ProjectSimulationService] = None,
|
4447
4457
|
require_human_approval: bool = False,
|
4448
4458
|
scheduling_service: Optional[SchedulingService] = None,
|
4459
|
+
stalled_ticket_timeout: Optional[int] = 60,
|
4449
4460
|
):
|
4450
4461
|
self.agent_service = agent_service
|
4451
4462
|
self.routing_service = routing_service
|
@@ -4463,11 +4474,37 @@ class QueryProcessor:
|
|
4463
4474
|
self.require_human_approval = require_human_approval
|
4464
4475
|
self._shutdown_event = asyncio.Event()
|
4465
4476
|
self.scheduling_service = scheduling_service
|
4477
|
+
self.stalled_ticket_timeout = stalled_ticket_timeout
|
4478
|
+
|
4479
|
+
self._stalled_ticket_task = None
|
4480
|
+
|
4481
|
+
# Start background task for stalled ticket detection if not already running
|
4482
|
+
if self.stalled_ticket_timeout is not None and self._stalled_ticket_task is None:
|
4483
|
+
try:
|
4484
|
+
self._stalled_ticket_task = asyncio.create_task(
|
4485
|
+
self._run_stalled_ticket_checks())
|
4486
|
+
except RuntimeError:
|
4487
|
+
# No running event loop - likely in test environment
|
4488
|
+
pass
|
4466
4489
|
|
4467
4490
|
async def process(
|
4468
4491
|
self, user_id: str, user_text: str, timezone: str = None
|
4469
4492
|
) -> AsyncGenerator[str, None]:
|
4470
4493
|
"""Process the user request with appropriate agent and handle ticket management."""
|
4494
|
+
# Start background task for stalled ticket detection if not already running
|
4495
|
+
if self.stalled_ticket_timeout is not None and self._stalled_ticket_task is None:
|
4496
|
+
try:
|
4497
|
+
loop = asyncio.get_running_loop()
|
4498
|
+
self._stalled_ticket_task = loop.create_task(
|
4499
|
+
self._run_stalled_ticket_checks())
|
4500
|
+
except RuntimeError:
|
4501
|
+
# No running event loop - likely in test environment
|
4502
|
+
# Instead of just passing, log a message for clarity
|
4503
|
+
import logging
|
4504
|
+
logging.warning(
|
4505
|
+
"No running event loop available for stalled ticket checker.")
|
4506
|
+
# Don't try to create the task - this prevents the coroutine warning
|
4507
|
+
|
4471
4508
|
try:
|
4472
4509
|
# Handle human agent messages differently
|
4473
4510
|
if await self._is_human_agent(user_id):
|
@@ -4515,7 +4552,9 @@ class QueryProcessor:
|
|
4515
4552
|
except Exception as e:
|
4516
4553
|
print(f"Error in request processing: {str(e)}")
|
4517
4554
|
print(traceback.format_exc())
|
4518
|
-
yield
|
4555
|
+
# Use yield instead of direct function calling to avoid coroutine warning
|
4556
|
+
error_msg = "\n\nI apologize for the technical difficulty.\n\n"
|
4557
|
+
yield error_msg
|
4519
4558
|
|
4520
4559
|
async def _is_human_agent(self, user_id: str) -> bool:
|
4521
4560
|
"""Check if the user is a registered human agent."""
|
@@ -4583,6 +4622,69 @@ class QueryProcessor:
|
|
4583
4622
|
|
4584
4623
|
return response
|
4585
4624
|
|
4625
|
+
async def shutdown(self):
|
4626
|
+
"""Clean shutdown of the query processor."""
|
4627
|
+
self._shutdown_event.set()
|
4628
|
+
|
4629
|
+
# Cancel the stalled ticket task if running
|
4630
|
+
if hasattr(self, '_stalled_ticket_task') and self._stalled_ticket_task is not None:
|
4631
|
+
self._stalled_ticket_task.cancel()
|
4632
|
+
try:
|
4633
|
+
await self._stalled_ticket_task
|
4634
|
+
except (asyncio.CancelledError, TypeError):
|
4635
|
+
# Either properly cancelled coroutine or a mock that can't be awaited
|
4636
|
+
pass
|
4637
|
+
|
4638
|
+
async def _run_stalled_ticket_checks(self):
|
4639
|
+
"""Run periodic checks for stalled tickets."""
|
4640
|
+
try:
|
4641
|
+
while not self._shutdown_event.is_set():
|
4642
|
+
await self._check_for_stalled_tickets()
|
4643
|
+
# Check every 5 minutes or half the timeout period, whichever is smaller
|
4644
|
+
check_interval = min(
|
4645
|
+
300, self.stalled_ticket_timeout * 30) if self.stalled_ticket_timeout else 300
|
4646
|
+
await asyncio.sleep(check_interval)
|
4647
|
+
except asyncio.CancelledError:
|
4648
|
+
# Task was cancelled, clean exit
|
4649
|
+
pass
|
4650
|
+
except Exception as e:
|
4651
|
+
print(f"Error in stalled ticket check: {e}")
|
4652
|
+
|
4653
|
+
async def _check_for_stalled_tickets(self):
|
4654
|
+
"""Check for tickets that haven't been updated in a while and reassign them."""
|
4655
|
+
# If stalled ticket detection is disabled, exit early
|
4656
|
+
if self.stalled_ticket_timeout is None:
|
4657
|
+
return
|
4658
|
+
|
4659
|
+
# Find tickets that haven't been updated in the configured time
|
4660
|
+
stalled_cutoff = datetime.datetime.now(
|
4661
|
+
datetime.timezone.utc) - datetime.timedelta(minutes=self.stalled_ticket_timeout)
|
4662
|
+
|
4663
|
+
# Query for stalled tickets using the find_stalled_tickets method
|
4664
|
+
stalled_tickets = await self.ticket_service.ticket_repository.find_stalled_tickets(
|
4665
|
+
stalled_cutoff, [TicketStatus.ACTIVE, TicketStatus.TRANSFERRED]
|
4666
|
+
)
|
4667
|
+
|
4668
|
+
for ticket in stalled_tickets:
|
4669
|
+
# Re-route using routing service to find the optimal agent
|
4670
|
+
new_agent = await self.routing_service.route_query(ticket.query)
|
4671
|
+
|
4672
|
+
# Skip if the routing didn't change
|
4673
|
+
if new_agent == ticket.assigned_to:
|
4674
|
+
continue
|
4675
|
+
|
4676
|
+
# Process as handoff
|
4677
|
+
await self.handoff_service.process_handoff(
|
4678
|
+
ticket.id,
|
4679
|
+
ticket.assigned_to or "unassigned",
|
4680
|
+
new_agent,
|
4681
|
+
f"Automatically reassigned after {self.stalled_ticket_timeout} minutes of inactivity"
|
4682
|
+
)
|
4683
|
+
|
4684
|
+
# Log the reassignment
|
4685
|
+
print(
|
4686
|
+
f"Stalled ticket {ticket.id} reassigned from {ticket.assigned_to or 'unassigned'} to {new_agent}")
|
4687
|
+
|
4586
4688
|
async def _process_system_commands(
|
4587
4689
|
self, user_id: str, user_text: str
|
4588
4690
|
) -> Optional[str]:
|
@@ -4739,154 +4841,157 @@ class QueryProcessor:
|
|
4739
4841
|
)
|
4740
4842
|
return f"Project {ticket_id} has been {'approved' if approved else 'rejected'}."
|
4741
4843
|
|
4742
|
-
|
4743
|
-
|
4744
|
-
|
4745
|
-
|
4746
|
-
|
4747
|
-
|
4748
|
-
|
4749
|
-
|
4750
|
-
|
4751
|
-
|
4752
|
-
|
4753
|
-
|
4754
|
-
|
4755
|
-
|
4756
|
-
|
4757
|
-
|
4758
|
-
|
4759
|
-
|
4760
|
-
|
4761
|
-
|
4762
|
-
|
4763
|
-
|
4764
|
-
|
4765
|
-
|
4766
|
-
|
4767
|
-
|
4768
|
-
|
4769
|
-
|
4844
|
+
elif command == "!schedule" and args and self.scheduling_service:
|
4845
|
+
# Format: !schedule task_id [agent_id] [YYYY-MM-DD HH:MM]
|
4846
|
+
parts = args.strip().split(" ", 2)
|
4847
|
+
if len(parts) < 1:
|
4848
|
+
return "Usage: !schedule task_id [agent_id] [YYYY-MM-DD HH:MM]"
|
4849
|
+
|
4850
|
+
task_id = parts[0]
|
4851
|
+
agent_id = parts[1] if len(parts) > 1 else None
|
4852
|
+
time_str = parts[2] if len(parts) > 2 else None
|
4853
|
+
|
4854
|
+
# Fetch the task from ticket repository
|
4855
|
+
ticket = self.ticket_service.ticket_repository.get_by_id(
|
4856
|
+
task_id)
|
4857
|
+
if not ticket:
|
4858
|
+
return f"Task {task_id} not found."
|
4859
|
+
|
4860
|
+
# Convert ticket to scheduled task
|
4861
|
+
scheduled_task = ScheduledTask(
|
4862
|
+
task_id=task_id,
|
4863
|
+
title=ticket.query[:50] +
|
4864
|
+
"..." if len(ticket.query) > 50 else ticket.query,
|
4865
|
+
description=ticket.query,
|
4866
|
+
estimated_minutes=ticket.complexity.get(
|
4867
|
+
"estimated_minutes", 30) if ticket.complexity else 30,
|
4868
|
+
priority=5, # Default priority
|
4869
|
+
assigned_to=agent_id or ticket.assigned_to,
|
4870
|
+
# Use current agent as a specialization tag
|
4871
|
+
specialization_tags=[ticket.assigned_to],
|
4872
|
+
)
|
4770
4873
|
|
4771
|
-
|
4772
|
-
|
4773
|
-
|
4774
|
-
|
4775
|
-
|
4776
|
-
|
4777
|
-
|
4778
|
-
|
4779
|
-
|
4780
|
-
|
4874
|
+
# Set scheduled time if provided
|
4875
|
+
if time_str:
|
4876
|
+
try:
|
4877
|
+
scheduled_time = datetime.datetime.fromisoformat(
|
4878
|
+
time_str)
|
4879
|
+
scheduled_task.scheduled_start = scheduled_time
|
4880
|
+
scheduled_task.scheduled_end = scheduled_time + datetime.timedelta(
|
4881
|
+
minutes=scheduled_task.estimated_minutes
|
4882
|
+
)
|
4883
|
+
except ValueError:
|
4884
|
+
return "Invalid date format. Use YYYY-MM-DD HH:MM."
|
4781
4885
|
|
4782
|
-
|
4783
|
-
|
4784
|
-
|
4785
|
-
|
4886
|
+
# Schedule the task
|
4887
|
+
result = await self.scheduling_service.schedule_task(
|
4888
|
+
scheduled_task, preferred_agent_id=agent_id
|
4889
|
+
)
|
4786
4890
|
|
4787
|
-
|
4788
|
-
|
4789
|
-
|
4790
|
-
|
4791
|
-
|
4792
|
-
|
4793
|
-
|
4891
|
+
# Update ticket with scheduling info
|
4892
|
+
self.ticket_service.update_ticket_status(
|
4893
|
+
task_id,
|
4894
|
+
ticket.status,
|
4895
|
+
scheduled_start=result.scheduled_start,
|
4896
|
+
scheduled_agent=result.assigned_to
|
4897
|
+
)
|
4794
4898
|
|
4795
|
-
|
4796
|
-
|
4797
|
-
|
4798
|
-
|
4799
|
-
|
4800
|
-
|
4899
|
+
# Format response
|
4900
|
+
response = "# Task Scheduled\n\n"
|
4901
|
+
response += f"**Task:** {scheduled_task.title}\n"
|
4902
|
+
response += f"**Assigned to:** {result.assigned_to}\n"
|
4903
|
+
response += f"**Scheduled start:** {result.scheduled_start.strftime('%Y-%m-%d %H:%M')}\n"
|
4904
|
+
response += f"**Estimated duration:** {result.estimated_minutes} minutes"
|
4801
4905
|
|
4802
|
-
|
4906
|
+
return response
|
4803
4907
|
|
4804
|
-
|
4805
|
-
|
4806
|
-
|
4807
|
-
|
4808
|
-
|
4809
|
-
|
4908
|
+
elif command == "!timeoff" and args and self.scheduling_service:
|
4909
|
+
# Format: !timeoff request YYYY-MM-DD HH:MM YYYY-MM-DD HH:MM reason
|
4910
|
+
# or: !timeoff cancel request_id
|
4911
|
+
parts = args.strip().split(" ", 1)
|
4912
|
+
if len(parts) < 2:
|
4913
|
+
return "Usage: \n- !timeoff request START_DATE END_DATE reason\n- !timeoff cancel request_id"
|
4810
4914
|
|
4811
|
-
|
4812
|
-
|
4915
|
+
action = parts[0].lower()
|
4916
|
+
action_args = parts[1]
|
4813
4917
|
|
4814
|
-
|
4815
|
-
|
4816
|
-
|
4817
|
-
|
4818
|
-
|
4918
|
+
if action == "request":
|
4919
|
+
# Parse request args
|
4920
|
+
request_parts = action_args.split(" ", 2)
|
4921
|
+
if len(request_parts) < 3:
|
4922
|
+
return "Usage: !timeoff request YYYY-MM-DD YYYY-MM-DD reason"
|
4819
4923
|
|
4820
|
-
|
4821
|
-
|
4822
|
-
|
4924
|
+
start_str = request_parts[0]
|
4925
|
+
end_str = request_parts[1]
|
4926
|
+
reason = request_parts[2]
|
4823
4927
|
|
4824
|
-
|
4825
|
-
|
4826
|
-
|
4827
|
-
|
4828
|
-
|
4829
|
-
|
4830
|
-
|
4831
|
-
|
4832
|
-
|
4833
|
-
|
4928
|
+
try:
|
4929
|
+
start_time = datetime.datetime.fromisoformat(start_str)
|
4930
|
+
end_time = datetime.datetime.fromisoformat(end_str)
|
4931
|
+
except ValueError:
|
4932
|
+
return "Invalid date format. Use YYYY-MM-DD HH:MM."
|
4933
|
+
|
4934
|
+
# Submit time off request
|
4935
|
+
success, status, request_id = await self.scheduling_service.request_time_off(
|
4936
|
+
user_id, start_time, end_time, reason
|
4937
|
+
)
|
4834
4938
|
|
4835
|
-
|
4836
|
-
|
4837
|
-
|
4838
|
-
|
4939
|
+
if success:
|
4940
|
+
return f"Time off request submitted and automatically approved. Request ID: {request_id}"
|
4941
|
+
else:
|
4942
|
+
return f"Time off request {status}. Request ID: {request_id}"
|
4839
4943
|
|
4840
|
-
|
4841
|
-
|
4842
|
-
|
4843
|
-
|
4844
|
-
|
4944
|
+
elif action == "cancel":
|
4945
|
+
request_id = action_args.strip()
|
4946
|
+
success, status = await self.scheduling_service.cancel_time_off_request(
|
4947
|
+
user_id, request_id
|
4948
|
+
)
|
4845
4949
|
|
4846
|
-
|
4847
|
-
|
4848
|
-
|
4849
|
-
|
4950
|
+
if success:
|
4951
|
+
return f"Time off request {request_id} cancelled successfully."
|
4952
|
+
else:
|
4953
|
+
return f"Failed to cancel request: {status}"
|
4850
4954
|
|
4851
|
-
|
4955
|
+
return "Unknown timeoff action. Use 'request' or 'cancel'."
|
4852
4956
|
|
4853
|
-
|
4854
|
-
|
4855
|
-
|
4856
|
-
|
4957
|
+
elif command == "!schedule-view" and self.scheduling_service:
|
4958
|
+
# View agent's schedule for the next week
|
4959
|
+
# Default to current user if no agent specified
|
4960
|
+
agent_id = args.strip() if args else user_id
|
4857
4961
|
|
4858
|
-
|
4859
|
-
|
4962
|
+
start_time = datetime.datetime.now(datetime.timezone.utc)
|
4963
|
+
end_time = start_time + datetime.timedelta(days=7)
|
4860
4964
|
|
4861
|
-
|
4862
|
-
|
4863
|
-
|
4864
|
-
|
4965
|
+
# Get tasks for the specified time period
|
4966
|
+
tasks = await self.scheduling_service.get_agent_tasks(
|
4967
|
+
agent_id, start_time, end_time, include_completed=False
|
4968
|
+
)
|
4865
4969
|
|
4866
|
-
|
4867
|
-
|
4970
|
+
if not tasks:
|
4971
|
+
return f"No scheduled tasks found for {agent_id} in the next 7 days."
|
4868
4972
|
|
4869
|
-
|
4870
|
-
|
4973
|
+
# Sort by start time
|
4974
|
+
tasks.sort(
|
4975
|
+
key=lambda t: t.scheduled_start or datetime.datetime.max)
|
4871
4976
|
|
4872
|
-
|
4873
|
-
|
4977
|
+
# Format response
|
4978
|
+
response = f"# Schedule for {agent_id}\n\n"
|
4874
4979
|
|
4875
|
-
|
4876
|
-
|
4877
|
-
|
4878
|
-
|
4879
|
-
|
4980
|
+
current_day = None
|
4981
|
+
for task in tasks:
|
4982
|
+
# Group by day
|
4983
|
+
task_day = task.scheduled_start.strftime(
|
4984
|
+
"%Y-%m-%d") if task.scheduled_start else "Unscheduled"
|
4880
4985
|
|
4881
|
-
|
4882
|
-
|
4883
|
-
|
4986
|
+
if task_day != current_day:
|
4987
|
+
response += f"\n## {task_day}\n\n"
|
4988
|
+
current_day = task_day
|
4884
4989
|
|
4885
|
-
|
4886
|
-
|
4887
|
-
|
4990
|
+
start_time = task.scheduled_start.strftime(
|
4991
|
+
"%H:%M") if task.scheduled_start else "TBD"
|
4992
|
+
response += f"- **{start_time}** ({task.estimated_minutes} min): {task.title}\n"
|
4888
4993
|
|
4889
|
-
|
4994
|
+
return response
|
4890
4995
|
|
4891
4996
|
return None
|
4892
4997
|
|
@@ -5589,7 +5694,8 @@ class SolanaAgentFactory:
|
|
5589
5694
|
project_approval_service=project_approval_service,
|
5590
5695
|
project_simulation_service=project_simulation_service,
|
5591
5696
|
require_human_approval=config.get("require_human_approval", False),
|
5592
|
-
scheduling_service=scheduling_service
|
5697
|
+
scheduling_service=scheduling_service,
|
5698
|
+
stalled_ticket_timeout=config.get("stalled_ticket_timeout", 60),
|
5593
5699
|
)
|
5594
5700
|
|
5595
5701
|
return query_processor
|
@@ -5876,6 +5982,22 @@ class TenantManager:
|
|
5876
5982
|
llm_provider, task_planning_service, ticket_repo
|
5877
5983
|
)
|
5878
5984
|
|
5985
|
+
# Create scheduling repository and service
|
5986
|
+
tenant_db_adapter = self._create_tenant_db_adapter(
|
5987
|
+
tenant) # Get the DB adapter properly
|
5988
|
+
scheduling_repository = SchedulingRepository(
|
5989
|
+
tenant_db_adapter) # Use the correct adapter
|
5990
|
+
|
5991
|
+
scheduling_service = SchedulingService(
|
5992
|
+
scheduling_repository=scheduling_repository,
|
5993
|
+
task_planning_service=task_planning_service,
|
5994
|
+
agent_service=agent_service
|
5995
|
+
)
|
5996
|
+
|
5997
|
+
# Update task_planning_service with scheduling_service if needed
|
5998
|
+
if task_planning_service:
|
5999
|
+
task_planning_service.scheduling_service = scheduling_service
|
6000
|
+
|
5879
6001
|
# Create query processor
|
5880
6002
|
return QueryProcessor(
|
5881
6003
|
agent_service=agent_service,
|
@@ -5895,6 +6017,9 @@ class TenantManager:
|
|
5895
6017
|
require_human_approval=tenant.get_config_value(
|
5896
6018
|
"require_human_approval", False
|
5897
6019
|
),
|
6020
|
+
scheduling_service=scheduling_service,
|
6021
|
+
stalled_ticket_timeout=tenant.get_config_value(
|
6022
|
+
"stalled_ticket_timeout"),
|
5898
6023
|
)
|
5899
6024
|
|
5900
6025
|
def _deep_merge(self, target: Dict, source: Dict) -> None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: solana-agent
|
3
|
-
Version: 11.
|
3
|
+
Version: 11.2.0
|
4
4
|
Summary: The Future of Work
|
5
5
|
License: MIT
|
6
6
|
Keywords: ai,openai,ai agents,agi
|
@@ -263,7 +263,6 @@ Solana Agent transforms organizations into living systems that continuously lear
|
|
263
263
|
Tool registry for AI agent capability extension.
|
264
264
|
Permission-based tool access for security and control.
|
265
265
|
Clean interface for third-party integrations through standard Python APIs.
|
266
|
-
Built-in internet search capabilities via Perplexity API.
|
267
266
|
Runtime tool discovery without service restarts.
|
268
267
|
|
269
268
|
## Implementation Technologies
|
@@ -325,7 +324,7 @@ config = {
|
|
325
324
|
"api_key": "your-pinecone-key",
|
326
325
|
"index": "your-index"
|
327
326
|
},
|
328
|
-
"
|
327
|
+
"stalled_ticket_timeout": 60,
|
329
328
|
"ai_agents": [
|
330
329
|
{
|
331
330
|
"name": "research_specialist",
|
@@ -0,0 +1,6 @@
|
|
1
|
+
solana_agent/__init__.py,sha256=zpfnWqANd3OHGWm7NCF5Y6m01BWG4NkNk8SK9Ex48nA,18
|
2
|
+
solana_agent/ai.py,sha256=SPHPCQPqk1wuBymB3StNRYp84s9pAa7LOeROBSHiJfQ,243608
|
3
|
+
solana_agent-11.2.0.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
|
4
|
+
solana_agent-11.2.0.dist-info/METADATA,sha256=quIlcgag0-hHIAy7-ibsh-JWxpBmravXIeoCp4zXxDw,18468
|
5
|
+
solana_agent-11.2.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
6
|
+
solana_agent-11.2.0.dist-info/RECORD,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
solana_agent/__init__.py,sha256=zpfnWqANd3OHGWm7NCF5Y6m01BWG4NkNk8SK9Ex48nA,18
|
2
|
-
solana_agent/ai.py,sha256=5bmnf1ipsdk3aQsiYFDhLUSVyUCQPamxAJ6QEawKp5s,237431
|
3
|
-
solana_agent-11.1.0.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
|
4
|
-
solana_agent-11.1.0.dist-info/METADATA,sha256=ijTLk43YwFb_fC-zvOGnxVgv4hFBCprnHzB0J6lYxQA,18528
|
5
|
-
solana_agent-11.1.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
6
|
-
solana_agent-11.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|