summaryrefslogtreecommitdiff
path: root/ai_api_module.py
diff options
context:
space:
mode:
Diffstat (limited to 'ai_api_module.py')
-rwxr-xr-xai_api_module.py183
1 files changed, 183 insertions, 0 deletions
diff --git a/ai_api_module.py b/ai_api_module.py
new file mode 100755
index 0000000..16f80df
--- /dev/null
+++ b/ai_api_module.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+# COPYRIGHT 2025 Thomas Grothe
+import argparse
+import requests
+import json
+import os
+import sys
+import http.client
+from util import *
+
+#this is a python module to provide REST API access to some AI model
+# use a CLI tool or custom webtool to use it
+# docs: TODO
+
+# each function shall return a tuple containing [success bool, response dict]
+
+### CONFIGURATION HERE (yes you could move this into a config file if desired, but i don't need that currently)
+# Base URL and endpoints configuration
+
+# the key of the environment variable used to store your api key
+api_key_env_key = 'GEMINI_API_KEY'
+
+# the api key, if you prefer to set it here
+api_key = None
+
+urlbase = 'https://generativelanguage.googleapis.com/v1beta/' #models/gemini-2.0-flash:generateContent'
+endpoints = {
+ 'chat': {
+ 'simple': urlbase+'/chat/completions', # without custom assistant #use components/schemas/ChatCompletionRequest
+ 'assistant': urlbase+'/assistant/--assistant_id--/chat/completions', #use components/schemas/AssistantChatCompletionRequest
+ },
+ 'model': {
+ 'list': urlbase+'/models',
+ },
+ 'assistant': {
+ 'list': urlbase+'/assistants'
+ },
+ 'conversation': {
+ 'list': urlbase+'/conversations',
+ 'get': urlbase+'/conversation/' #/message_id
+ },
+ 'genContent': 'models/gemini-2.0-flash:generateContent?key='
+}
+
+# Possible environment variables for API key
+
+#the default message context. this will be appended to or replaced as conversation goes, or if using a previous chat
+messages = [
+ {
+ 'role': 'system',
+ 'content': '', #TODO system prompt instructions
+ },
+]
+
+def setupURLParams():
+ urlbase += f'?key={api_key}'
+
+def get_api_key():
+ global api_key
+ """Try to get API key from environment variables"""
+ if api_key == None:
+ if os.environ.get(api_key_env_key):
+ api_key = os.environ.get(api_key_env_key)
+ return api_key
+ return None
+
+def list_models(output_mode = ''):
+ headers = {"Content-Type": "application/json"}
+ url = endpoints['model']['list'] + f'?key={api_key}'
+ print(f'calling {url}')
+ response = requests.get(f"{endpoints['model']['list']}", headers=headers)
+ models = []
+
+ if response.status_code == 200:
+ j = json.loads(response.text)
+ if 'j' in output_mode:
+ return j
+ else:
+ for d in j['data']:
+ models.append(d)
+ return [True, models]
+ else:
+ return [False, response]
+
+def list_assistants(output_mode = ''):
+ headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
+ response = requests.get(f"{endpoints['assistant']['list']}", headers=headers)
+ if response.status_code == 200:
+ j = json.loads(response.text) # keys: assistants, total_assistants, scores,carousel_display_name,carousel_subtitle
+ if 'j' in output_mode:
+ return j
+ else:
+ res = []
+ for a in j['assistants']:
+ res.append(a)
+ return [True, res]
+ else:
+ return [False, response]
+
+def list_conversations(output_mode = ''):
+ headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
+ response = requests.get(f"{endpoints['conversation']['list']}", headers=headers)
+ if response.status_code == 200:
+ j = json.loads(response.text)
+ if 'j' in output_mode:
+ return j
+ else:
+ res = []
+ for a in j.keys(): #iterate through assistants
+ for cid in a: #j[]: #convo ids for each assistant
+ print('todo')
+ #TODO this gives only the first message, with its id, then we have to call /conversation/{m_id}
+
+ return [True, res]
+
+def get_conversations(msg_id, output_mode = ''):
+ headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
+ response = requests.get(f"{endpoints['conversation']['get']}", headers=headers)
+
+
+def query(model_id, query, top_p, temperature, stream=False):
+ """Call the model with some query"""
+ headers = {"Content-Type": "application/json"} #, "Authorization": f"Bearer {api_key}"}
+ url = urlbase+endpoints['genContent']+str(api_key)
+ #attachments = [] #file attachments
+
+ payload = { #todo options for other parameters
+ "contents": [
+ {
+ "parts": [
+ {
+ "text": query
+ }
+ ]
+ }
+ ]
+ }
+ # payload = {
+ # "model": model_id,
+ # "top_p": top_p,
+ # "temperature": temperature,
+ # "stream": stream,
+ # "messages": [{"role": "user", "content": query}], #TODO include previous conversation here
+ # # components/schemas/ChatCompletionsMessage-Input
+ # # content: string
+ # # attachments:
+ # # role: user, assistant, or system
+ # # name:
+ # # tool_call_id
+ # # tool_calls
+
+ #}
+
+ if stream:
+ response = requests.post(url, json=payload, headers=headers, stream=True)
+ for line in response.iter_lines():
+ if line:
+ line = line.decode('utf-8')
+ if line.startswith("data: ") and not line.startswith("data: [DONE]"):
+ data = line[len("data: "):]
+ try:
+ content = json.loads(data)["choices"][0]["delta"]["content"]
+ if content:
+ log(content)
+ sys.stdout.write(content)
+ sys.stdout.flush()
+ except (json.JSONDecodeError, KeyError, IndexError):
+ pass
+ print()
+ else:
+ response = requests.post(url, json=payload, headers=headers)
+ if response.status_code == 200:
+ response_json = response.json()
+ #TODO handle message to append
+ response = response_json['candidates'][0]['content']['parts'][0]['text']
+ log(response)
+ return response
+ #return response_json["choices"][0]["message"]["content"]
+ else:
+ print(f"Error: {response.status_code}")
+ print(response.text)
+ log(response.text)
+ return None