diff options
Diffstat (limited to '')
| -rw-r--r-- | cogs/jarvis.py | 218 |
1 files changed, 128 insertions, 90 deletions
diff --git a/cogs/jarvis.py b/cogs/jarvis.py index 0096b38..b199c8a 100644 --- a/cogs/jarvis.py +++ b/cogs/jarvis.py | |||
| @@ -29,126 +29,164 @@ class Jarvis(commands.Cog): | |||
| 29 | 29 | ||
| 30 | if message.mentions: | 30 | if message.mentions: |
| 31 | if message.mentions[0].id == self.bot.user.id: | 31 | if message.mentions[0].id == self.bot.user.id: |
| 32 | content = await self.get_original(message) | 32 | await self.jarvis(message) |
| 33 | await self.jarvis(message, content) | ||
| 34 | 33 | ||
| 35 | elif "jarvis" in message.content.lower(): | 34 | elif "jarvis" in message.content.lower(): |
| 36 | content = await self.get_original(message) | 35 | await self.jarvis(message) |
| 37 | await self.jarvis(message, content) | ||
| 38 | 36 | ||
| 39 | @staticmethod | 37 | @staticmethod |
| 40 | async def jarvis(message, content): | 38 | async def jarvis(message): |
| 41 | url = "https://openrouter.ai/api/v1/chat/completions" | ||
| 42 | headers = { | 39 | headers = { |
| 43 | 40 | "Authorization": f"Bearer {os.getenv('OPENROUTER_API_KEY')}", | |
| 44 | "Authorization": f"Bearer {os.getenv("OPENROUTER_API_KEY")}", | ||
| 45 | "Content-Type": "application/json" | 41 | "Content-Type": "application/json" |
| 46 | } | 42 | } |
| 47 | 43 | ||
| 48 | # Build messages array | 44 | messages = Jarvis._build_messages(message) |
| 49 | messages = [ | 45 | payload = Jarvis._build_payload(messages) |
| 50 | { | 46 | |
| 51 | "role": "system", | 47 | response_data = await Jarvis._make_api_request(payload, headers) |
| 52 | "content": "your task is to tell the program what to do and how to respond after receiving a message.\n" | 48 | response_message, action = Jarvis._parse_response(response_data) |
| 53 | "message field has to be filled out in polish.\n" | 49 | |
| 54 | "act like jarvis, be a personal assistant to user, do not use emojis\n" | 50 | channel = message.channel |
| 55 | "do not ask the user if they want to do an action. the user will tell you that" | 51 | await channel.send(response_message) |
| 56 | } | 52 | |
| 57 | ] | 53 | if action == "download": |
| 54 | await Jarvis._handle_download_action(message, channel) | ||
| 55 | |||
| 56 | @staticmethod | ||
| 57 | def _build_messages(message): | ||
| 58 | messages = [{ | ||
| 59 | "role": "system", | ||
| 60 | "content": "your task is to tell the program what to do and how to respond after receiving a message.\n" | ||
| 61 | "message field has to be filled out in polish.\n" | ||
| 62 | "act like jarvis, be a personal assistant to user, do not use emojis\n" | ||
| 63 | "do not ask the user if they want to do an action. the user will tell you that" | ||
| 64 | }] | ||
| 58 | 65 | ||
| 59 | # Add original message with display name | ||
| 60 | messages.append({ | 66 | messages.append({ |
| 61 | "role": "user", | 67 | "role": "user", |
| 62 | "content": f"[from {message.author.display_name}]: {message.content}" | 68 | "content": f"[from {message.author.display_name}]: {message.content}" |
| 63 | }) | 69 | }) |
| 64 | 70 | ||
| 65 | # If it's a reply, add the original message and reply as separate messages | 71 | if Jarvis._is_reply(message): |
| 66 | if message.reference and (message.type == discord.MessageType.reply or message.reference.resolved): | 72 | messages.extend(Jarvis._build_reply_messages(message)) |
| 67 | if message.reference.resolved: | 73 | |
| 68 | original_msg = message.reference.resolved | 74 | return messages |
| 69 | messages.append({ | 75 | |
| 70 | "role": "user", | 76 | @staticmethod |
| 71 | "content": f"[original message from {original_msg.author.display_name}]: {original_msg.content}" | 77 | def _is_reply(message): |
| 72 | }) | 78 | return message.reference and (message.type == discord.MessageType.reply or message.reference.resolved) |
| 73 | messages.append({ | 79 | |
| 74 | "role": "user", | 80 | @staticmethod |
| 75 | "content": f"[reply from {message.author.display_name}]: {message.content}" | 81 | def _build_reply_messages(message): |
| 76 | }) | 82 | original_msg = message.reference.resolved |
| 77 | 83 | return [ | |
| 78 | payload = { | 84 | { |
| 85 | "role": "user", | ||
| 86 | "content": f"[original message from {original_msg.author.display_name}]: {original_msg.content}" | ||
| 87 | }, | ||
| 88 | { | ||
| 89 | "role": "user", | ||
| 90 | "content": f"[reply from {message.author.display_name}]: {message.content}" | ||
| 91 | } | ||
| 92 | ] | ||
| 93 | |||
| 94 | @staticmethod | ||
| 95 | def _build_payload(messages): | ||
| 96 | return { | ||
| 79 | "model": "arcee-ai/trinity-large-preview:free", | 97 | "model": "arcee-ai/trinity-large-preview:free", |
| 80 | "messages": messages, | 98 | "messages": messages, |
| 81 | "response_format": { | 99 | "response_format": Jarvis._get_response_schema(), |
| 82 | "type": "json_schema", | 100 | "plugins": [{"id": "response-healing"}] |
| 83 | "json_schema": { | 101 | } |
| 84 | "name": "response", | 102 | |
| 85 | "strict": True, | 103 | @staticmethod |
| 86 | "schema": { | 104 | def _get_response_schema(): |
| 87 | "type": "object", | 105 | return { |
| 88 | "properties": { | 106 | "type": "json_schema", |
| 89 | "message": { | 107 | "json_schema": { |
| 90 | "type": "string", | 108 | "name": "response", |
| 91 | "description": "the message that user will receive", | 109 | "strict": True, |
| 92 | }, | 110 | "schema": { |
| 93 | "action": { | 111 | "type": "object", |
| 94 | "type": "string", | 112 | "properties": { |
| 95 | "description": "the action to do, either download, compress or none" | 113 | "message": { |
| 96 | } | 114 | "type": "string", |
| 115 | "description": "the message that user will receive", | ||
| 97 | }, | 116 | }, |
| 98 | "required": ["message", "action"], | 117 | "action": { |
| 99 | "additionalProperties": False, | 118 | "type": "string", |
| 119 | "description": "the action to do, either download, compress or none" | ||
| 100 | } | 120 | } |
| 101 | } | 121 | }, |
| 102 | }, | 122 | "required": ["message", "action"], |
| 103 | "plugins": [ | 123 | "additionalProperties": False, |
| 104 | {"id": "response-healing"} | 124 | } |
| 105 | ] | 125 | } |
| 106 | } | 126 | } |
| 107 | 127 | ||
| 128 | @staticmethod | ||
| 129 | async def _make_api_request(payload, headers): | ||
| 130 | url = "https://openrouter.ai/api/v1/chat/completions" | ||
| 108 | response = requests.post(url=url, headers=headers, json=payload) | 131 | response = requests.post(url=url, headers=headers, json=payload) |
| 109 | # Parse the JSON response | ||
| 110 | response_data = response.json() | 132 | response_data = response.json() |
| 111 | logger.info(response_data) | 133 | logger.info(response_data) |
| 112 | # Extract the content string | 134 | return response_data |
| 113 | content = response_data['choices'][0]['message']['content'] | ||
| 114 | 135 | ||
| 115 | # Remove the code block markers and parse the inner JSON | 136 | @staticmethod |
| 137 | def _parse_response(response_data): | ||
| 138 | content = response_data['choices'][0]['message']['content'] | ||
| 116 | inner_json_str = content.strip('```json\n').strip('```').strip() | 139 | inner_json_str = content.strip('```json\n').strip('```').strip() |
| 117 | inner_data = json.loads(inner_json_str) | 140 | inner_data = json.loads(inner_json_str) |
| 118 | 141 | ||
| 119 | # Now you can access the message and action | 142 | response_message = inner_data['message'] |
| 120 | response_message = inner_data['message'] # "Proszę podać plik do kompresji lub link do pobrania." | 143 | action = inner_data['action'] |
| 121 | action = inner_data['action'] # "none OR download OR compress" | 144 | |
| 122 | channel = message.channel | ||
| 123 | logger.info(response_message) | 145 | logger.info(response_message) |
| 124 | logger.info(action) | 146 | logger.info(action) |
| 147 | |||
| 148 | return response_message, action | ||
| 125 | 149 | ||
| 126 | await channel.send(response_message) | 150 | @staticmethod |
| 151 | async def _handle_download_action(message, channel): | ||
| 152 | url_match = Jarvis._extract_url(message.content) | ||
| 153 | if not url_match: | ||
| 154 | await channel.send("Nie znaleziono poprawnego linku w odpowiedzi.") | ||
| 155 | return | ||
| 127 | 156 | ||
| 128 | if action == "download": | 157 | response = await Downloader.download_standalone(url=url_match) |
| 129 | # Extract the URL from the message using regex | 158 | await Jarvis._process_download_response(response, channel) |
| 130 | url_match = re.search(r'https://(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]{1,63}\.[A-Za-z]{2,63}/\S*', message.content).group(0) | 159 | |
| 131 | if not url_match: | 160 | @staticmethod |
| 132 | await channel.send("Nie znaleziono poprawnego linku w odpowiedzi.") | 161 | def _extract_url(content): |
| 133 | return | 162 | match = re.search(r'https://(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]{1,63}\.[A-Za-z]{2,63}/\S*', content) |
| 134 | 163 | return match.group(0) if match else None | |
| 135 | response = await Downloader.download_standalone(url=url_match) | 164 | |
| 136 | if isinstance(response, dict): | 165 | @staticmethod |
| 137 | if response["status"] == "success": | 166 | async def _process_download_response(response, channel): |
| 138 | if response["type"] == "url": | 167 | if not isinstance(response, dict): |
| 139 | await channel.send(f"pobioron i wysyłon: {response['content']}") | 168 | return |
| 140 | elif response["type"] == "file": | 169 | |
| 141 | file_data = response["content"] | 170 | if response["status"] == "success": |
| 142 | await channel.send("wysyłon:", file=discord.File(file_data)) | 171 | await Jarvis._handle_successful_download(response, channel) |
| 143 | shutil.rmtree(response["temp_dir"]) | 172 | else: |
| 144 | else: | 173 | await Jarvis._handle_failed_download(response) |
| 145 | logger.error(f"Error: {response['content']}") | 174 | |
| 146 | if 'details' in response: | 175 | @staticmethod |
| 147 | logger.error(f"Details: {response['details']}") | 176 | async def _handle_successful_download(response, channel): |
| 148 | shutil.rmtree(response["temp_dir"]) | 177 | if response["type"] == "url": |
| 149 | 178 | await channel.send(f"pobioron i wysyłon: {response['content']}") | |
| 150 | elif action == "compress": | 179 | elif response["type"] == "file": |
| 151 | pass | 180 | file_data = response["content"] |
| 181 | await channel.send("wysyłon:", file=discord.File(file_data)) | ||
| 182 | shutil.rmtree(response["temp_dir"]) | ||
| 183 | |||
| 184 | @staticmethod | ||
| 185 | async def _handle_failed_download(response): | ||
| 186 | logger.error(f"Error: {response['content']}") | ||
| 187 | if 'details' in response: | ||
| 188 | logger.error(f"Details: {response['details']}") | ||
| 189 | shutil.rmtree(response["temp_dir"]) | ||
| 152 | 190 | ||
| 153 | @staticmethod | 191 | @staticmethod |
| 154 | async def get_original(message): | 192 | async def get_original(message): |
