Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/llm client #255

Merged
merged 5 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 5 additions & 0 deletions services/llm-client/adapters/db/interactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from models.interactions import Interaction, InteractionRequest
from utils import json

def request_to_model(wire):
return InteractionRequest(wire.id, json.deserialize(wire.interaction, Interaction) , wire.response, wire.request_date, wire.status, wire.timeout)
4 changes: 4 additions & 0 deletions services/llm-client/adapters/db/prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from models.prompts import Prompt

def to_model(wire) -> Prompt:
return Prompt(wire.prompt_name, wire.prompt)
Empty file.
Binary file not shown.
Binary file not shown.
5 changes: 5 additions & 0 deletions services/llm-client/adapters/inputs/interaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from wires.inputs.interactions import Interaction as WireInteraction
from models.interactions import Interaction as ModelInteraction

def dto_to_model(dto: WireInteraction) -> ModelInteraction:
return ModelInteraction(dto.requestId, dto.promptName, dto.variables, dto.images)
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
33 changes: 33 additions & 0 deletions services/llm-client/diplomat/db/interactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from components.scylla_connection import ScyllaConnection
from datetime import datetime, timezone
from adapters.db import interactions as interactions_db_adapter
from utils import json

def get_interaction(interaction_id, scylla: ScyllaConnection):
query = "select * from execution.interaction_requests where id = ?;"
prepared_statement = scylla.session.prepare(query)
rows = scylla.session.execute(prepared_statement, (interaction_id,))
return interactions_db_adapter.request_to_model(rows[0]) if rows else None

def insert(interaction, scylla: ScyllaConnection):
print(json.serialize(interaction))
statement = """INSERT INTO execution.interaction_requests (id, interaction, response, request_date, status, timeout)
VALUES (?, ?, ?, ?, ?, ?)"""
interaction_id = interaction.id
interaction_value = json.serialize(interaction)
response = None
request_date = int(datetime.now(timezone.utc).timestamp() * 1000)
status = 'pending'
timeout = 30000
prepared_statement = scylla.session.prepare(statement)
scylla.session.execute(prepared_statement, (interaction_id, interaction_value, response, request_date, status, timeout))


def update_interaction(interaction_result, scylla: ScyllaConnection):
statement = """UPDATE execution.interaction_requests
SET response = %s, status = %s
WHERE id = %s"""
interaction_id = interaction_result.id
response = interaction_result.response
status = interaction_result.status
scylla.session.execute(statement, (response, status, interaction_id))
8 changes: 8 additions & 0 deletions services/llm-client/diplomat/db/prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from components.scylla_connection import ScyllaConnection
from adapters.db import prompts as prompts_db_adapter

def get_prompt(prompt_name, scylla: ScyllaConnection):
query = "select * from configuration.prompts where prompt_name = ?;"
prepared_statement = scylla.session.prepare(query)
rows = scylla.session.execute(prepared_statement, (prompt_name,))
return prompts_db_adapter.to_model(rows[0]) if rows else None
10 changes: 3 additions & 7 deletions services/llm-client/diplomat/http_server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from fastapi import FastAPI
from wires.inputs.interactions import Interaction
from flows.interactions import new_interaction
from adapters.inputs.interaction import dto_to_model
from components.component_manager import ComponentManager
from components.scylla_connection import ScyllaConnection

Expand All @@ -15,10 +17,4 @@ async def shutdown_event():

@app.post("/api/llm/interactions")
def greet(interaction: Interaction):
a = ComponentManager.get_component(ScyllaConnection)
query = "select * from configuration.prompts where prompt_name = 'tick_shopping_items' ;"
prepared = a.session.prepare(query)
#bound = prepared.bind(('value',)) # Replace 'value' with the actual value you want to query
rows = a.session.execute(query)
for row in rows:
print(row)
return new_interaction(dto_to_model(interaction), ComponentManager.get_component(ScyllaConnection))
19 changes: 0 additions & 19 deletions services/llm-client/diplomat/scylla.py

This file was deleted.

Empty file.
Binary file not shown.
Binary file not shown.
34 changes: 34 additions & 0 deletions services/llm-client/flows/interactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

from components.scylla_connection import ScyllaConnection
from datetime import datetime, timezone
from models.interactions import Interaction, InteractionResult
from diplomat.db import interactions as interactions_db
from diplomat.db import prompts as prompts_db
from logic import interactions as interactions_logic

def new_interaction(interaction: Interaction, scylla: ScyllaConnection):
existent_interaction = interactions_db.get_interaction(interaction.id, scylla)
if(existent_interaction is not None):
if(interactions_logic.is_final_state(existent_interaction)):
return existent_interaction
else:
if(interactions_logic.is_timed_out(existent_interaction, int(datetime.now(timezone.utc).timestamp() * 1000))):
existent_interaction.failed()
interactions_db.update_interaction(InteractionResult(existent_interaction.id, 'failed', None), scylla)
return existent_interaction
else:
return existent_interaction

prompt = prompts_db.get_prompt(interaction.prompt_name, scylla)
if(prompt is None):
return None

# fill prompt with variables
interactions_db.insert(interaction, scylla)
# request Open IA
# update the database
# return
return interaction



Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 5 additions & 0 deletions services/llm-client/logic/interactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def is_final_state(interaction):
return interaction.status == 'completed' or interaction.status == 'failed'

def is_timed_out(interaction, date_epoch: int):
return interaction.request_date + interaction.timeout < date_epoch
6 changes: 6 additions & 0 deletions services/llm-client/logic/prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from models.prompts import Prompt
import re

def find_variables(prompt : Prompt):
pattern = r'\{\{(\w+)\}\}'
return re.findall(pattern, prompt.prompt)
2 changes: 1 addition & 1 deletion services/llm-client/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import uvicorn

if __name__ == "__main__":
import uvicorn
uvicorn.run("diplomat.http_server:app", host="127.0.0.1", port=8000)
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ def run_migration(session):
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};
""")
session.execute("""
CREATE TABLE IF NOT EXISTS execution.interactions (
interaction_id UUID PRIMARY KEY,
input text,
CREATE TABLE IF NOT EXISTS execution.interaction_requests (
id UUID PRIMARY KEY,
interaction text,
response text,
request_date date,
status int,
request_date bigint,
status text,
timeout int
);
""")
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
29 changes: 29 additions & 0 deletions services/llm-client/models/interactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from uuid import UUID

class Interaction:
def __init__(self, id: UUID, prompt_name:str, variables:dict, images:dict, message:str = None):
self.id = id
self.prompt_name = prompt_name
self.variables = variables
self.images = images
self.message = message

class InteractionRequest:
def __init__(self, id, interaction, response, request_date, status, timeout):
self.id = id
self.interaction = interaction
self.response = response
self.request_date = request_date
self.status = status
self.timeout = timeout

def failed(self):
self.status = 'failed'
return self


class InteractionResult:
def __init__(self, id, status, response):
self.id = id
self.status = status
self.response = response
4 changes: 4 additions & 0 deletions services/llm-client/models/prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Prompt:
def __init__(self, prompt_name:str, prompt:str):
self.prompt_name = prompt_name
self.prompt = prompt
2 changes: 2 additions & 0 deletions services/llm-client/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
python_paths = llm-client
7 changes: 7 additions & 0 deletions services/llm-client/readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Requirements
- cassandra-driver
- pip install 'uvicorn[standard]'
- pip install fastapi
- pip install pytest

docker run -d --name some-scylla -p 9042:9042 scylladb/scylla
docker exec -it some-scylla cqlsh
python .\main.py

Needs:
Get variables from the OS
Expand Down
Empty file.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
23 changes: 23 additions & 0 deletions services/llm-client/tests/logic/prompts_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import pytest
from logic import prompts
from models.prompts import Prompt

def test_find_variables_basic():
prompt = Prompt("","Hello {{name}}, how are you?")
assert prompts.find_variables(prompt) == ['name']

def test_find_multiple_variables():
prompt = Prompt("", "{{name}} and {{age}} are variables.")
assert prompts.find_variables(prompt) == ['name', 'age']

def test_no_variables():
prompt = Prompt("", "There are no variables here.")
assert prompts.find_variables(prompt) == []

def test_edge_case_empty_string():
prompt = Prompt("", "")
assert prompts.find_variables(prompt) == []

def test_edge_case_empty_braces():
prompt = Prompt("", "{{}} should not be considered a variable.")
assert prompts.find_variables(prompt) == []
Empty file.
Binary file not shown.
Binary file not shown.
35 changes: 35 additions & 0 deletions services/llm-client/utils/json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
from uuid import UUID
from typing import Type, Dict, Any

def serialize(obj):
data = _create_key_value_pairs(obj)
return json.dumps(data, cls=_UUIDEncoder)

def deserialize(obj: str, type: Type):
data = _json_to_dict(obj)
return _dict_to_class(data, type)

class _UUIDEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, UUID):
return str(obj)
return super().default(obj)

def _create_key_value_pairs(obj):
if hasattr(obj, '__dict__'):
return vars(obj)
else:
raise TypeError("Object does not have a __dict__ attribute")

def _json_to_dict(json_string: str) -> Dict[str, Any]:
return json.loads(json_string)

def _dict_to_class(d: Dict[str, Any], cls: Type) -> Any:
annotations = getattr(cls, '__annotations__', {})

for key, value in d.items():
if annotations.get(key) == UUID and isinstance(value, str):
d[key] = UUID(value)

return cls(**d)
Binary file not shown.
4 changes: 3 additions & 1 deletion services/llm-client/wires/inputs/interactions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pydantic import BaseModel
from typing import Dict, Any
from uuid import UUID

class Interaction(BaseModel):
promptName: str
variables: Dict[str, Any] = {}
images: Dict[str, Any] = {}
images: Dict[str, Any] = {}
requestId: UUID
Loading