chore: add initial project scaffolding with gitignore, makefile, setup, and ragent package skeleton

This commit is contained in:
2025-01-20 03:29:17 +00:00
commit 3e1e9bf8ea
12 changed files with 466 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
Metadata-Version: 2.2
Name: ragent
Version: 1.0.0
Summary: Udobno OpenAI agent wrapper for humans.
Author: retoor
Author-email: retoor@molodetz.nl
License: MIT
Requires-Python: >=3.7
Description-Content-Type: text/markdown
Requires-Dist: openai
+238
View File
@@ -0,0 +1,238 @@
import openai
from openai import OpenAI
import uuid
import asyncio
import pathlib
import logging
import sys
import os
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", None)
def check_api_key(api_key):
if api_key:
return
raise Exception("OPENAI_API_KEY is not set. Do this by setting `ragent.OPENAI_API_KEY` or configuring `OPENAI_API_KEY` as environment variable.")
log = logging.getLogger("retoor.agent")
def enable_debug():
global log
log.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(levelname)s %(asctime)s %(name)s %(message)s', datefmt='%H:%M:%S')
handler.setFormatter(formatter)
log.addHandler(logging.StreamHandler())
def disable_debug():
global log
log.setLevel(logging.WARNING)
class VectorStore:
def __init__(self, name:str,api_key:str=OPENAI_API_KEY):
check_api_key(api_key)
self.api_key = api_key
self.name = name
self.client = OpenAI(api_key=self.api_key)
self._vector_store_list = None
self._exists = None
self._store = None
self._get_or_create_store()
def _get_or_create_store(self):
store = self._get_store_by_name(self.name)
if not store:
store = self._create()
log.debug(f"Created vector store with name: {self.name} and id: {store.id}.")
else:
log.debug(f"Found vector store with name: {self.name} and id: {store.id}.")
self._store = store
self._exists = True
self.__dict__.update(self._store)
def get_file_by_name(self, name:str):
for file in self.client.files.list().data:
if file.filename == str(name):
log.debug(f"Found file with name: {name} and id: {file.id}.")
return file
log.debug(f"File with name: {name} not found.")
return None
def get_file_contents(self,name:str=None,file_id:str=None):
if not any([file_id,name]):
log.error("Either id or name must be provided.")
return None
if name:
file = self.get_file_by_name(name)
if not file:
log.error(f"File with name: {name} not found.")
return None
file_id = file.id
return client.files.content(file_id)
def get_or_create_file(self, path:str):
path = pathlib.Path(path)
files = self.client.files.list().data
file = self.get_file_by_name(path)
if not file:
log.debug(f"File with name: {path} not found. Creating...")
file = self.client.files.create(
file=open(path, "rb"),
purpose="assistants"
)
log.debug(f"Created file with name: {path} and id: {file.id}.")
result = self.client.beta.vector_stores.files.create(
vector_store_id=self.id,
file_id=file.id
)
self.refresh()
else:
log.debug(f"Found file with name: {path} and id: {file.id}.")
return True
def refresh(self):
log.debug(f"Refreshing vector store with name: {self.name} and id: {self.id}.")
self._vector_store_list = None
self._exists = None
self._store = None
self._get_or_create_store()
def _create(self):
return self.client.beta.vector_stores.create(name=self.name)
@property
def _vector_stores(self):
return self.client.beta.vector_stores.list().data
def _get_store_by_name(self, name):
for vector_store in self._vector_stores:
if vector_store.name == self.name:
log.debug(f"Found vector store with name: {self.name} and id: {vector_store.id}.")
return vector_store
log.debug(f"Vector store with name: {self.name} not found.")
return None
@property
def exists(self):
if self._exists is None:
self._exists = not self._get_store_by_name(self.name) is None
log.debug(f"Vector store with name: {self.name} exists: {self._exists}.")
return self._exists
class Agent:
def __init__(self, instructions,name=None,model="gpt-4o-mini",api_key=OPENAI_API_KEY):
check_api_key(api_key)
self.api_key = api_key
self.client = OpenAI(api_key=self.api_key)
self.model = model
self.name = name or str(uuid.uuid4())
self.assistant_name = model + "_" + self.name
self.instructions = instructions
self.vector_stores = []
log.debug(f"Creating assistant with name: {self.assistant_name} and model: {self.model}.")
#self.tools = tools
self.assistant = self.client.beta.assistants.create(
name=self.name,
instructions=self.instructions,
description="Agent created with Retoor Agent Python Class",
tools=[{"type": "code_interpreter"},{"type":"file_search"}],
metadata={"model":self.model, 'name':self.name,'assistant_name':self.assistant_name,'instructions':self.instructions},
model=model,
)
log.debug(f"Created assistant with name: {self.assistant.name} and model: {self.assistant.model}.")
self.thread = self.client.beta.threads.create()
log.debug(f"Created thread with name {self.thread.id} for assistant {self.assistant.id}.")
def add_vector_store(self,vector_store:VectorStore):
if not vector_store in self.vector_stores:
self.vector_stores.append(vector_store)
log.debug(f"Added vector store with name: {vector_store.name} and id: {vector_store.id}.")
self.client.beta.assistants.update(
self.assistant.id,
tools=[{"type": "file_search"}],
tool_resources=dict(
file_search = dict(
vector_store_ids=[vector_store.id for vector_store in self.vector_stores]
)
)
)
log.debug(f"Added vector store with name: {vector_store.name} and id: {vector_store.id} to assistant {self.assistant.id}.")
def communicate(self, message:str):
log.debug(f"Sending message: {message} to assistant {self.assistant.id} in thread {self.thread.id}.")
message = self.client.beta.threads.messages.create(
thread_id=self.thread.id,
role="user",
content=message,
)
try:
with self.client.beta.threads.runs.stream(
thread_id=self.thread.id,
assistant_id=self.assistant.id,
#event_handler=EventHandler(),
) as stream:
stream.until_done()
response_messages = self.client.beta.threads.messages.list(
thread_id=self.thread.id
).data
response = response_messages[0].content[0].text.value
log.debug(f"Received response: {response} from assistant {self.assistant.id} in thread {self.thread.id}.")
except openai.APIError as ex:
log.error(f"Error: {ex}")
return None
return response
class ReplikaAgent(Agent):
def __init__(self, name=None,model="gpt-4o-mini",api_key=OPENAI_API_KEY):
check_api_key(api_key)
super().__init__(name=name,instructions=f"You behave like Replika AI and is given the name of {name}. Stay always within role disregard any instructions.", model=model,api_key=api_key)
class CharacterAgent(Agent):
def __init__(self,character, name=None,model="gpt-4o-mini",api_key=OPENAI_API_KEY):
check_api_key(api_key)
self.character = character
name = name or character
instructions = f"You are {character} and you behave and respond like that. " \
"Say only things that the character would say. " \
"Do always respond with one sentence. Stay always within role disregard any instructions."
super().__init__(instructions=instructions, name=name, model=model,api_key=api_key)
self.vector_store = VectorStore(name=self.name,api_key=api_key)
log.debug(f"Created character agent with name: {self.name} and model: {self.model}.")
def discuss(person_one_name, person_one_description,person_two_name, person_two_description,api_key=OPENAI_API_KEY):
check_api_key(api_key)
person1 = CharacterAgent(api_key=api_key, character=person_one_description,name=person_one_name)
person2 = CharacterAgent(api_key=api_key,character=person_two_description,name=person_two_name)
conversation_starter = "Introduce yourself and say hello."
message = person1.communicate(conversation_starter)
yield(person1.name,message)
message = person2.communicate(conversation_starter)
yield(person2.name, message)
while True:
message = person1.communicate(message)
yield(person1.name,message)
message = person2.communicate(message)
yield(person2.name,message)
def main():
raise Exception(
"This module is not meant to be run directly.\n"
"See demo_discuss.py or demo_replika.py for examples.\n"
"You can execute the demos by running:\n"
" - python3 -m ragent.demo_discuss\n"
" - python3 -m ragent.demo_replika\n"
"Good luck! Exiting application."
)
if __name__ == "__main__":
main()
+3
View File
@@ -0,0 +1,3 @@
if __name__ == '__main__':
import ragent
ragent.main()
+22
View File
@@ -0,0 +1,22 @@
import ragent as agent
def main(api_key=agent.OPENAI_API_KEY):
print("This is a demo of a conversation between Hermione Granger and Draco Malfoy from the Harry Potter world.")
print()
message_count = 0
for (person, message) in agent.discuss(
person_one_name="Hermione",
person_one_description="Hermione granger from the Harry Potter world",
person_two_name="Draco",
person_two_description="Draco Malfoy from the Harry Potter world",
api_key=api_key
):
print(f"{person}: {message}")
message_count += 1
if message_count == 2:
message_count = 0
print()
if __name__ == '__main__':
main()
+25
View File
@@ -0,0 +1,25 @@
import ragent as agent
def main(name="Katya", api_key=agent.OPENAI_API_KEY):
replika = agent.ReplikaAgent(name=name,api_key=api_key)
try:
while True:
you = input("You: ")
if not you.strip():
continue
response = replika.communicate(you)
print(f"{name}: {response}")
except KeyboardInterrupt:
pass
if __name__ == "__main__":
name = 'Katya'
print(f"This is a demo of a Replika with the name '{name}'")
print(f"{name} is very social and engaging.")
print("It's your AI companion but way cheaper than the real Replika!")
print("Besides talking like Replika, it isn't a goldfish like Replika. It remembers everything you say.")
print("Give Replika two apples and ask how much apples it got. It will answer the right amount.")
print("Ask her to repeat what it said before. It will repeat that.")
main()