GraziePrego commited on
Commit
63a321d
·
verified ·
1 Parent(s): a87833b

Upload api/api_message.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. api/api_message.py +182 -3
api/api_message.py CHANGED
@@ -1,3 +1,182 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:cea8b79ef90c0e20ad02ea4b9b139fb955680c4614acc49b3e578aff15c18a4e
3
- size 7623
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ import uuid
4
+ from datetime import datetime, timedelta
5
+ from agent import AgentContext, UserMessage, AgentContextType
6
+ from helpers.api import ApiHandler, Request, Response
7
+ from helpers import files, projects
8
+ from helpers.print_style import PrintStyle
9
+ from helpers.projects import activate_project
10
+ from helpers.security import safe_filename
11
+ from initialize import initialize_agent
12
+ import threading
13
+
14
+
15
+ class ApiMessage(ApiHandler):
16
+ # Track chat lifetimes for cleanup
17
+ _chat_lifetimes = {}
18
+ _cleanup_lock = threading.Lock()
19
+
20
+ @classmethod
21
+ def requires_auth(cls) -> bool:
22
+ return False # No web auth required
23
+
24
+ @classmethod
25
+ def requires_csrf(cls) -> bool:
26
+ return False # No CSRF required
27
+
28
+ @classmethod
29
+ def requires_api_key(cls) -> bool:
30
+ return True # Require API key
31
+
32
+ async def process(self, input: dict, request: Request) -> dict | Response:
33
+ # Extract parameters
34
+ context_id = input.get("context_id", "")
35
+ message = input.get("message", "")
36
+ attachments = input.get("attachments", [])
37
+ lifetime_hours = input.get("lifetime_hours", 24) # Default 24 hours
38
+ project_name = input.get("project_name", None)
39
+ agent_profile = input.get("agent_profile", None)
40
+
41
+ # Set an agent if profile provided
42
+ override_settings = {}
43
+ if agent_profile:
44
+ override_settings["agent_profile"] = agent_profile
45
+
46
+ if not message:
47
+ return Response('{"error": "Message is required"}', status=400, mimetype="application/json")
48
+
49
+ # Handle attachments (base64 encoded)
50
+ attachment_paths = []
51
+ if attachments:
52
+ upload_folder_int = "/a0/usr/uploads"
53
+ upload_folder_ext = files.get_abs_path("usr/uploads")
54
+ os.makedirs(upload_folder_ext, exist_ok=True)
55
+
56
+ for attachment in attachments:
57
+ if not isinstance(attachment, dict) or "filename" not in attachment or "base64" not in attachment:
58
+ continue
59
+
60
+ try:
61
+ filename = safe_filename(attachment["filename"])
62
+ if not filename:
63
+ raise ValueError("Invalid filename")
64
+
65
+ # Decode base64 content
66
+ file_content = base64.b64decode(attachment["base64"])
67
+
68
+ # Save to temp file
69
+ save_path = os.path.join(upload_folder_ext, filename)
70
+ with open(save_path, "wb") as f:
71
+ f.write(file_content)
72
+
73
+ attachment_paths.append(os.path.join(upload_folder_int, filename))
74
+ except Exception as e:
75
+ PrintStyle.error(f"Failed to process attachment {attachment.get('filename', 'unknown')}: {e}")
76
+ continue
77
+
78
+ # Get or create context
79
+ if context_id:
80
+ context = AgentContext.use(context_id)
81
+ if not context:
82
+ return Response('{"error": "Context not found"}', status=404, mimetype="application/json")
83
+
84
+ # Validation: if agent profile is provided, it must match the exising
85
+ if agent_profile and context.agent0.config.profile != agent_profile:
86
+ return Response('{"error": "Cannot override agent profile on existing context"}', status=400, mimetype="application/json")
87
+
88
+
89
+ # Validation: if project is provided but context already has different project
90
+ existing_project = context.get_data(projects.CONTEXT_DATA_KEY_PROJECT)
91
+ if project_name and existing_project and existing_project != project_name:
92
+ return Response('{"error": "Project can only be set on first message"}', status=400, mimetype="application/json")
93
+ else:
94
+ config = initialize_agent(override_settings=override_settings)
95
+ context = AgentContext(config=config, type=AgentContextType.USER)
96
+ AgentContext.use(context.id)
97
+ context_id = context.id
98
+ # Activate project if provided
99
+ if project_name:
100
+ try:
101
+ activate_project(context_id, project_name)
102
+ except Exception as e:
103
+ # Handle project or context errors more gracefully
104
+ error_msg = str(e)
105
+ PrintStyle.error(f"Failed to activate project '{project_name}' for context '{context_id}': {error_msg}")
106
+ return Response(
107
+ f'{{"error": "Failed to activate project \\"{project_name}\\""}}',
108
+ status=500,
109
+ mimetype="application/json",
110
+ )
111
+
112
+ # Activate project if provided
113
+ if project_name:
114
+ try:
115
+ projects.activate_project(context_id, project_name)
116
+ except Exception as e:
117
+ return Response(f'{{"error": "Failed to activate project: {str(e)}"}}', status=400, mimetype="application/json")
118
+
119
+ # Update chat lifetime
120
+ with self._cleanup_lock:
121
+ self._chat_lifetimes[context_id] = datetime.now() + timedelta(hours=lifetime_hours)
122
+
123
+ # Process message
124
+ try:
125
+ # Log the message
126
+ attachment_filenames = [os.path.basename(path) for path in attachment_paths] if attachment_paths else []
127
+
128
+ PrintStyle(
129
+ background_color="#6C3483", font_color="white", bold=True, padding=True
130
+ ).print("External API message:")
131
+ PrintStyle(font_color="white", padding=False).print(f"> {message}")
132
+ if attachment_filenames:
133
+ PrintStyle(font_color="white", padding=False).print("Attachments:")
134
+ for filename in attachment_filenames:
135
+ PrintStyle(font_color="white", padding=False).print(f"- {filename}")
136
+
137
+ # Add user message to chat history so it's visible in the UI
138
+ msg_id = str(uuid.uuid4())
139
+ context.log.log(
140
+ type="user",
141
+ heading="",
142
+ content=message,
143
+ kvps={"attachments": attachment_filenames},
144
+ id=msg_id,
145
+ )
146
+
147
+ # Send message to agent
148
+ task = context.communicate(UserMessage(message=message, attachments=attachment_paths, id=msg_id))
149
+ result = await task.result()
150
+
151
+ # Clean up expired chats
152
+ self._cleanup_expired_chats()
153
+
154
+ return {
155
+ "context_id": context_id,
156
+ "response": result
157
+ }
158
+
159
+ except Exception as e:
160
+ PrintStyle.error(f"External API error: {e}")
161
+ return Response(f'{{"error": "{str(e)}"}}', status=500, mimetype="application/json")
162
+
163
+ @classmethod
164
+ def _cleanup_expired_chats(cls):
165
+ """Clean up expired chats"""
166
+ with cls._cleanup_lock:
167
+ now = datetime.now()
168
+ expired_contexts = [
169
+ context_id for context_id, expiry in cls._chat_lifetimes.items()
170
+ if now > expiry
171
+ ]
172
+
173
+ for context_id in expired_contexts:
174
+ try:
175
+ context = AgentContext.get(context_id)
176
+ if context:
177
+ context.reset()
178
+ AgentContext.remove(context_id)
179
+ del cls._chat_lifetimes[context_id]
180
+ PrintStyle().print(f"Cleaned up expired chat: {context_id}")
181
+ except Exception as e:
182
+ PrintStyle.error(f"Failed to cleanup chat {context_id}: {e}")