Skip to content

Commit

Permalink
Add Python support to calling the API
Browse files Browse the repository at this point in the history
  • Loading branch information
cybermaggedon committed Nov 22, 2024
1 parent 7a64385 commit a744653
Show file tree
Hide file tree
Showing 2 changed files with 339 additions and 0 deletions.
3 changes: 3 additions & 0 deletions trustgraph-base/trustgraph/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

from . api import *

336 changes: 336 additions & 0 deletions trustgraph-base/trustgraph/api/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@

import requests
import json
import dataclasses
import base64

from trustgraph.knowledge import hash

class ProtocolException(Exception):
pass

class ApplicationException(Exception):
pass

class Uri(str):
def is_uri(self): return True
def is_literal(self): return False

class Literal(str):
def is_uri(self): return False
def is_literal(self): return True

@dataclasses.dataclass
class Triple:
s : str
p : str
o : str

class Api:

def __init__(self, url="http://localhost:8088/"):

self.url = url

if not url.endswith("/"):
self.url += "/"

self.url += "api/v1/"

def check_error(self, response):

if "error" in response:

try:
msg = response["error"]["message"]
tp = response["error"]["message"]
except:
raise ApplicationException(
"Error, but the error object is broken"
)

raise ApplicationException(f"{tp}: {msg}")

def text_completion(self, system, prompt):

# The input consists of system and prompt strings
input = {
"system": system,
"prompt": prompt
}

url = f"{self.url}text-completion"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

try:
# Parse the response as JSON
object = resp.json()
except:
raise ProtocolException(f"Expected JSON response")

self.check_error(resp)

try:
return object["response"]
except:
raise ProtocolException(f"Response not formatted correctly")

def agent(self, question):

# The input consists of a question
input = {
"question": question
}

url = f"{self.url}agent"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

try:
# Parse the response as JSON
object = resp.json()
except:
raise ProtocolException(f"Expected JSON response")

self.check_error(resp)

try:
return object["answer"]
except:
raise ProtocolException(f"Response not formatted correctly")

def graph_rag(self, question):

# The input consists of a question
input = {
"query": question
}

url = f"{self.url}graph-rag"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

try:
# Parse the response as JSON
object = resp.json()
except:
raise ProtocolException(f"Expected JSON response")

self.check_error(resp)

try:
return object["response"]
except:
raise ProtocolException(f"Response not formatted correctly")

def embeddings(self, text):

# The input consists of a text block
input = {
"text": text
}

url = f"{self.url}embeddings"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

try:
# Parse the response as JSON
object = resp.json()
except:
raise ProtocolException(f"Expected JSON response")

self.check_error(resp)

try:
return object["vectors"]
except:
raise ProtocolException(f"Response not formatted correctly")

def prompt(self, id, variables):

# The input consists of system and prompt strings
input = {
"id": id,
"variables": variables
}

url = f"{self.url}prompt"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

try:
# Parse the response as JSON
object = resp.json()
except:
raise ProtocolException("Expected JSON response")

self.check_error(resp)

if "text" in object:
return object["text"]

if "object" in object:
try:
return json.loads(object["object"])
except Exception as e:
raise ProtocolException(
"Returned object not well-formed JSON"
)

raise ProtocolException("Response not formatted correctly")

def triples_query(self, s=None, p=None, o=None, limit=10000):

# The input consists of system and prompt strings
input = {
"limit": limit
}

if s: input["s"] = s
if p: input["p"] = p
if o: input["o"] = o

url = f"{self.url}triples-query"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

try:
# Parse the response as JSON
object = resp.json()
except:
raise ProtocolException("Expected JSON response")

self.check_error(resp)

if "response" not in object:
raise ProtocolException("Response not formatted correctly")

def to_value(x):
if x["e"]: return Uri(x["v"])
return Literal(x["v"])

return [
Triple(
s=to_value(t["s"]),
p=to_value(t["p"]),
o=to_value(t["o"])
)
for t in object["response"]
]

return object["response"]

def load_document(self, document, id=None, metadata=None):

if id is None:

if metadata is not None:

# Situation makes no sense. What can the metadata possibly
# mean if the caller doesn't know the document ID.
# Metadata should relate to the document by ID
raise RuntimeError("Can't specify metadata without id")

id = hash(document)

triples = []

def emit(t):
triples.append(t)

if metadata:
metadata.emit(
lambda t: triples.append({
"s": t.s.value,
"p": t.p.value,
"o": t.o.value
})
)

input = {
"id": id,
"metadata": triples,
"data": base64.b64encode(document).decode("utf-8"),
}

url = f"{self.url}load/document"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

def load_text(self, text, id=None, metadata=None, charset="utf-8"):

if id is None:

if metadata is not None:

# Situation makes no sense. What can the metadata possibly
# mean if the caller doesn't know the document ID.
# Metadata should relate to the document by ID
raise RuntimeError("Can't specify metadata without id")

id = hash(text)

triples = []

if metadata:
metadata.emit(
lambda t: triples.append({
"s": t.s.value,
"p": t.p.value,
"o": t.o.value
})
)

input = {
"id": id,
"metadata": triples,
"charset": charset,
"text": base64.b64encode(text).decode("utf-8"),
}

url = f"{self.url}load/text"

# Invoke the API, input is passed as JSON
resp = requests.post(url, json=input)

# Should be a 200 status code
if resp.status_code != 200:
raise ProtocolException(f"Status code {resp.status_code}")

0 comments on commit a744653

Please sign in to comment.