Documentation Fixes and Discord Example
This commit is contained in:
parent
9ea87cdb71
commit
b5301e6f9d
37
README.md
37
README.md
|
|
@ -1,9 +1,9 @@
|
|||
# Honcho
|
||||

|
||||

|
||||
[](https://discord.gg/plasticlabs)
|
||||

|
||||

|
||||

|
||||
[](https://twitter.com/plastic_labs)
|
||||
|
||||
A User context management solution for building AI Agents and LLM powered
|
||||
applications.
|
||||
|
|
@ -39,26 +39,27 @@ specifying the appropriate environment variables.
|
|||
1. Create a virtualenv and install the API's dependencies
|
||||
|
||||
```bash
|
||||
cd honcho/api/
|
||||
source $(poetry run poetry env info --path)/bin/activate
|
||||
poetry install
|
||||
cd honcho/api/ # change to the api directory
|
||||
poetry shell # Activate virutal environment
|
||||
poetry install # install dependencies
|
||||
```
|
||||
|
||||
2. Edit the `.env.template` file to specify the type of database and
|
||||
connection_uri. For testing sqlite is fine.
|
||||
2. Copy the `.env.template` file and specify the type of database and
|
||||
connection_uri. For testing sqlite is fine. The below example uses an
|
||||
in-memory sqlite database.
|
||||
|
||||
> Honcho has been tested with Postgresql and SQLite
|
||||
|
||||
```env
|
||||
DATABASE_TYPE=sqlite
|
||||
CONNECTION_URI=sqlite:///./honcho.db
|
||||
CONNECTION_URI=sqlite://
|
||||
```
|
||||
|
||||
3. Run the API via uvicorn
|
||||
|
||||
```bash
|
||||
cd honcho/api
|
||||
source $(poetry run poetry env info --path)/bin/activate
|
||||
cd honcho/api # change to the api directory
|
||||
poetry shell # Activate virtual environment if not already enabled
|
||||
python -m uvicorn src.main:app --reload
|
||||
```
|
||||
|
||||
|
|
@ -78,16 +79,18 @@ docker run --env-file .env -p 8000:8000 honcho-api:latest
|
|||
|
||||
#### Deploy on Fly
|
||||
|
||||
The API can also be deployed on fly.io with the following commands:
|
||||
The API can also be deployed on fly.io. Follow the [Fly.io
|
||||
Docs](https://fly.io/docs/getting-started/) to setup your environment and the
|
||||
`flyctl`.
|
||||
|
||||
|
||||
Once `flyctl` is set up use the the following commands to launch the application:
|
||||
|
||||
```bash
|
||||
cd honcho/api
|
||||
fly launch
|
||||
```
|
||||
|
||||
You can also add your secrets with the following shortcut command:
|
||||
```bash
|
||||
cat .env | fly secrets import
|
||||
flyctl launch --no-deploy # Follow the prompts and edit as you see fit
|
||||
cat .env | flyctl secrets import # Load in your secrets
|
||||
flyctl deploy # Deploy with appropriate environment variables
|
||||
```
|
||||
|
||||
### Client SDK
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
DATABASE_TYPE=sqlite
|
||||
CONNECTION_URI=sqlite:///./sql_db.db
|
||||
CONNECTION_URI=sqlite://
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from typing import Optional
|
||||
from sqlalchemy.orm import Session
|
||||
import uvicorn
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ def get_db():
|
|||
########################################################
|
||||
|
||||
@app.get("/users/{user_id}/sessions", response_model=list[schemas.Session])
|
||||
def get_sessions(user_id: str, location_id: str, db: Session = Depends(get_db)):
|
||||
def get_sessions(user_id: str, location_id: Optional[str] = None, db: Session = Depends(get_db)):
|
||||
"""Get All Sessions for a User
|
||||
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
# https://pythonspeed.com/articles/base-image-python-docker-images/
|
||||
# https://testdriven.io/blog/docker-best-practices/
|
||||
FROM python:3.10-slim-bullseye
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# https://stackoverflow.com/questions/53835198/integrating-python-poetry-with-docker
|
||||
ENV PYTHONFAULTHANDLER=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONHASHSEED=random \
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100 \
|
||||
POETRY_VERSION=1.4.1
|
||||
|
||||
RUN pip install "poetry==$POETRY_VERSION"
|
||||
|
||||
# Copy only requirements to cache them in docker layer
|
||||
WORKDIR /app
|
||||
COPY poetry.lock pyproject.toml /app/
|
||||
|
||||
# Project initialization:
|
||||
RUN poetry config virtualenvs.create false \
|
||||
&& poetry install --no-root --no-interaction --no-ansi --without dev
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN addgroup --system app && adduser --system --group app
|
||||
USER app
|
||||
|
||||
COPY . .
|
||||
|
||||
# https://stackoverflow.com/questions/29663459/python-app-does-not-print-anything-when-running-detached-in-docker
|
||||
CMD ["python", "-m", "uvicorn", "main:app", "--port", "80"]
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
class Mediator(ABC):
|
||||
@abstractmethod
|
||||
def get_sessions(self, user_id: str, location_id: str) -> List[Dict] | None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_session(self, session_id: str) -> Dict | None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_session(self, user_id: str, location_id: str, metadata: Dict) -> Dict:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update_session(self, session_id: str, metadata: Dict) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete_session(self, session_id: str) -> None:
|
||||
pass
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
"""
|
||||
Below is an implementation of a basic LRUcache that utilizes the built
|
||||
in OrderedDict data structure.
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
from mediator import SupabaseMediator
|
||||
import uuid
|
||||
from typing import List, Dict
|
||||
from langchain.schema import BaseMessage, Document
|
||||
from pydantic import BaseModel
|
||||
|
||||
# import sentry_sdk
|
||||
|
||||
|
||||
class Conversation:
|
||||
"Wrapper Class for storing contexts between channels. Using an object to pass by reference avoid additional cache hits"
|
||||
|
||||
# @sentry_sdk.trace
|
||||
def __init__(
|
||||
self,
|
||||
mediator: SupabaseMediator,
|
||||
user_id: str,
|
||||
session_id: str = str(uuid.uuid4()),
|
||||
metadata: Dict = {},
|
||||
):
|
||||
self.mediator: SupabaseMediator = mediator
|
||||
self.user_id = user_id
|
||||
self.session_id = session_id
|
||||
self.metadata: Dict = metadata
|
||||
|
||||
# @sentry_sdk.trace
|
||||
def add_message(
|
||||
self,
|
||||
message_type: str,
|
||||
message: BaseMessage,
|
||||
) -> None:
|
||||
self.mediator.add_message(self.session_id, message_type, message.content)
|
||||
|
||||
# @sentry_sdk.trace
|
||||
def get_messages(self, message_type: str) -> List[BaseMessage]:
|
||||
return self.mediator.get_messages(self.session_id, message_type)
|
||||
|
||||
# @sentry_sdk.trace
|
||||
def delete(self) -> None:
|
||||
self.mediator.delete_session(self.session_id)
|
||||
|
||||
# @sentry_sdk.trace
|
||||
# def restart(self) -> None:
|
||||
# self.delete()
|
||||
# representation = self.mediator.add_session(
|
||||
# user_id=self.user_id, location_id=self.location_id
|
||||
# )
|
||||
# self.session_id: str = representation["id"]
|
||||
# self.metadata = representation["metadata"]
|
||||
|
||||
# vector DB fn
|
||||
# @sentry_sdk.trace
|
||||
def add_texts(self, texts: List[str]) -> None:
|
||||
metadatas = [
|
||||
{"session_id": self.session_id, "user_id": self.user_id}
|
||||
for _ in range(len(texts))
|
||||
]
|
||||
self.mediator.vector_table.add_texts(texts, metadatas)
|
||||
|
||||
# vector DB fn
|
||||
# @sentry_sdk.trace
|
||||
def similarity_search(self, query: str, match_count: int = 5) -> List[Document]:
|
||||
return self.mediator.vector_table.similarity_search(
|
||||
query=query, k=match_count, filter={"user_id": self.user_id}
|
||||
)
|
||||
464
archive/chain.py
464
archive/chain.py
|
|
@ -1,464 +0,0 @@
|
|||
import os
|
||||
from langchain.chat_models import ChatOpenAI, AzureChatOpenAI
|
||||
from langchain.output_parsers.list import NumberedListOutputParser
|
||||
from langchain.prompts import (
|
||||
load_prompt,
|
||||
ChatPromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import AIMessage, HumanMessage, BaseMessage
|
||||
from dotenv import load_dotenv
|
||||
from collections.abc import AsyncIterator
|
||||
from cache import Conversation
|
||||
from typing import List
|
||||
|
||||
|
||||
from openai import BadRequestError
|
||||
|
||||
import sentry_sdk
|
||||
|
||||
load_dotenv()
|
||||
|
||||
SYSTEM_THOUGHT = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/thought.yaml")
|
||||
)
|
||||
SYSTEM_RESPONSE = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/response.yaml")
|
||||
)
|
||||
SYSTEM_THOUGHT_REVISION = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/thought_revision.yaml")
|
||||
)
|
||||
SYSTEM_USER_PREDICTION_THOUGHT = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/user_prediction_thought.yaml")
|
||||
)
|
||||
SYSTEM_USER_PREDICTION_THOUGHT_REVISION = load_prompt(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__), "prompts/user_prediction_thought_revision.yaml"
|
||||
)
|
||||
)
|
||||
SYSTEM_VOE_THOUGHT = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/voe_thought.yaml")
|
||||
)
|
||||
SYSTEM_VOE = load_prompt(os.path.join(os.path.dirname(__file__), "prompts/voe.yaml"))
|
||||
SYSTEM_CHECK_VOE_LIST = load_prompt(
|
||||
os.path.join(os.path.dirname(__file__), "prompts/check_voe_list.yaml")
|
||||
)
|
||||
|
||||
|
||||
class BloomChain:
|
||||
"Wrapper class for encapsulating the multiple different chains used in reasoning for the tutor's thoughts"
|
||||
llm: AzureChatOpenAI = AzureChatOpenAI(
|
||||
deployment_name=os.environ["OPENAI_API_DEPLOYMENT_NAME"],
|
||||
temperature=1.2,
|
||||
model_kwargs={"top_p": 0.5},
|
||||
)
|
||||
parser_llm: AzureChatOpenAI = AzureChatOpenAI(
|
||||
deployment_name=os.environ["OPENAI_API_DEPLOYMENT_NAME"]
|
||||
)
|
||||
system_voe_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_VOE_THOUGHT
|
||||
)
|
||||
system_voe: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_VOE
|
||||
)
|
||||
system_check_voe_list: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_CHECK_VOE_LIST
|
||||
)
|
||||
system_thought: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_THOUGHT
|
||||
)
|
||||
system_thought_revision: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_THOUGHT_REVISION
|
||||
)
|
||||
system_response: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
|
||||
prompt=SYSTEM_RESPONSE
|
||||
)
|
||||
system_user_prediction_thought: SystemMessagePromptTemplate = (
|
||||
SystemMessagePromptTemplate(prompt=SYSTEM_USER_PREDICTION_THOUGHT)
|
||||
)
|
||||
system_user_prediction_thought_revision: SystemMessagePromptTemplate = (
|
||||
SystemMessagePromptTemplate(prompt=SYSTEM_USER_PREDICTION_THOUGHT_REVISION)
|
||||
)
|
||||
|
||||
output_parser = NumberedListOutputParser()
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
def think(cls, cache: Conversation, input: str):
|
||||
"""Generate Bloom's thought on the user."""
|
||||
# load message history
|
||||
thought_prompt = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_thought,
|
||||
*cache.get_messages("thought"),
|
||||
HumanMessage(content=input),
|
||||
]
|
||||
)
|
||||
chain = thought_prompt | cls.llm
|
||||
|
||||
|
||||
def save_new_messages(ai_response):
|
||||
cache.add_message("thought", HumanMessage(content=input))
|
||||
cache.add_message("thought", AIMessage(content=ai_response))
|
||||
|
||||
return Streamable(
|
||||
chain.astream({}, {"tags": ["thought"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}),
|
||||
save_new_messages
|
||||
)
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
def revise_thought(cls, cache: Conversation, input: str, thought: str):
|
||||
"""Revise Bloom's thought about the user with retrieved personal data"""
|
||||
|
||||
# construct rag prompt, retrieve docs
|
||||
query = f"input: {input}\n thought: {thought}"
|
||||
docs = cache.similarity_search(query)
|
||||
|
||||
messages = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_thought_revision,
|
||||
*cache.get_messages("thought_revision"),
|
||||
HumanMessage(content=input),
|
||||
]
|
||||
)
|
||||
chain = messages | cls.llm
|
||||
|
||||
def save_new_messages(ai_response):
|
||||
cache.add_message("thought_revision", HumanMessage(content=input))
|
||||
cache.add_message("thought_revision", AIMessage(content=ai_response))
|
||||
|
||||
return Streamable(
|
||||
chain.astream({ "thought": thought, "retrieved_vectors": "\n".join(doc.page_content for doc in docs)}, {"tags": ["thought_revision"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}),
|
||||
save_new_messages
|
||||
)
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
def respond(cls, cache: Conversation, thought: str, input: str):
|
||||
"""Generate Bloom's response to the user."""
|
||||
response_prompt = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_response,
|
||||
*cache.get_messages("response"),
|
||||
HumanMessage(content=input),
|
||||
]
|
||||
)
|
||||
chain = response_prompt | cls.llm
|
||||
|
||||
def save_new_messages(ai_response):
|
||||
cache.add_message("response", HumanMessage(content=input))
|
||||
cache.add_message("response", AIMessage(content=ai_response))
|
||||
|
||||
return Streamable(
|
||||
chain.astream({ "thought": thought }, {"tags": ["response"], "metadata": {"conversation_id": cache.conversation_id, "user_id": cache.user_id}}),
|
||||
save_new_messages
|
||||
)
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def think_user_prediction(cls, cache: Conversation, input: str):
|
||||
"""Generate a thought about what the user is going to say"""
|
||||
|
||||
messages = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_user_prediction_thought,
|
||||
]
|
||||
)
|
||||
chain = messages | cls.llm
|
||||
|
||||
history = unpack_messages(cache.get_messages("response"))
|
||||
|
||||
user_prediction_thought = await chain.ainvoke(
|
||||
{"history": history},
|
||||
{
|
||||
"tags": ["user_prediction_thought"],
|
||||
"metadata": {
|
||||
"conversation_id": cache.conversation_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cache.add_message("user_prediction_thought", user_prediction_thought)
|
||||
|
||||
return user_prediction_thought.content
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def revise_user_prediction_thought(
|
||||
cls, cache: Conversation, user_prediction_thought: str, input: str
|
||||
):
|
||||
"""Revise the thought about what the user is going to say based on retrieval of VoE facts"""
|
||||
|
||||
messages = ChatPromptTemplate.from_messages(
|
||||
[
|
||||
cls.system_user_prediction_thought_revision,
|
||||
]
|
||||
)
|
||||
chain = messages | cls.llm
|
||||
|
||||
# construct rag prompt, retrieve docs
|
||||
query = f"input: {input}\n thought: {user_prediction_thought}"
|
||||
docs = cache.similarity_search(query)
|
||||
|
||||
history = unpack_messages(cache.get_messages("response"))
|
||||
|
||||
user_prediction_thought_revision = await chain.ainvoke(
|
||||
{
|
||||
"history": history,
|
||||
"user_prediction_thought": user_prediction_thought,
|
||||
"retrieved_vectors": "\n".join(doc.page_content for doc in docs),
|
||||
},
|
||||
config={
|
||||
"tags": ["user_prediction_thought_revision"],
|
||||
"metadata": {
|
||||
"conversation_id": cache.conversation_id,
|
||||
"user_id": cache.user_id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cache.add_message(
|
||||
"user_prediction_thought_revision", user_prediction_thought_revision
|
||||
)
|
||||
|
||||
return user_prediction_thought_revision.content
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def think_violation_of_expectation(
|
||||
cls, cache: Conversation, inp: str, user_prediction_thought_revision: str
|
||||
) -> None:
|
||||
"""Assess whether expectation was violated, derive and store facts"""
|
||||
|
||||
# format prompt
|
||||
messages = ChatPromptTemplate.from_messages([cls.system_voe_thought])
|
||||
chain = messages | cls.llm
|
||||
|
||||
voe_thought = await chain.ainvoke(
|
||||
{
|
||||
"user_prediction_thought_revision": user_prediction_thought_revision,
|
||||
"actual": inp,
|
||||
},
|
||||
config={"tags": ["voe_thought"], "metadata": {"user_id": cache.user_id}},
|
||||
)
|
||||
|
||||
cache.add_message("voe_thought", voe_thought)
|
||||
|
||||
return voe_thought.content
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def violation_of_expectation(
|
||||
cls,
|
||||
cache: Conversation,
|
||||
inp: str,
|
||||
user_prediction_thought_revision: str,
|
||||
voe_thought: str,
|
||||
) -> None:
|
||||
"""Assess whether expectation was violated, derive and store facts"""
|
||||
|
||||
# format prompt
|
||||
messages = ChatPromptTemplate.from_messages([cls.system_voe])
|
||||
chain = messages | cls.llm
|
||||
|
||||
voe = await chain.ainvoke(
|
||||
{
|
||||
"ai_message": cache.get_messages("response")[-1].content,
|
||||
"user_prediction_thought_revision": user_prediction_thought_revision,
|
||||
"actual": inp,
|
||||
"voe_thought": voe_thought,
|
||||
},
|
||||
config={"tags": ["voe"], "metadata": {"user_id": cache.user_id}},
|
||||
)
|
||||
|
||||
cache.add_message("voe", voe)
|
||||
facts = cls.output_parser.parse(voe.content)
|
||||
return facts
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def check_voe_list(cls, cache: Conversation, facts: List[str]):
|
||||
"""Filter the facts to just new ones"""
|
||||
|
||||
# create the message object from prompt template
|
||||
messages = ChatPromptTemplate.from_messages([cls.system_check_voe_list])
|
||||
chain = messages | cls.llm
|
||||
|
||||
# unpack the list of strings into one string for similarity search
|
||||
# TODO: should we query 1 by 1 and append to an existing facts list?
|
||||
query = " ".join(facts)
|
||||
|
||||
# query the vector store
|
||||
existing_facts = cache.similarity_search(query, match_count=10)
|
||||
|
||||
filtered_facts = await chain.ainvoke(
|
||||
{
|
||||
"existing_facts": "\n".join(
|
||||
fact.page_content for fact in existing_facts
|
||||
),
|
||||
"facts": "\n".join(fact for fact in facts),
|
||||
},
|
||||
config={"tags": ["check_voe_list"], "metadata": {"user_id": cache.user_id}},
|
||||
)
|
||||
|
||||
data = cls.output_parser.parse(filtered_facts.content)
|
||||
|
||||
# if the check returned "None", write facts to cache
|
||||
if not data:
|
||||
cache.add_texts(facts)
|
||||
else:
|
||||
cache.add_texts(data)
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def chat(cls, cache: Conversation, inp: str) -> tuple[str, str]:
|
||||
# VoE has to happen first. If there's user prediction history, derive and store fact(s)
|
||||
if cache.get_messages("user_prediction_thought_revision"):
|
||||
user_prediction_thought_revision = cache.get_messages(
|
||||
"user_prediction_thought_revision"
|
||||
)[-1].content
|
||||
|
||||
voe_thought = await cls.think_violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision
|
||||
)
|
||||
voe_facts = await cls.violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision, voe_thought
|
||||
)
|
||||
|
||||
if not voe_facts or voe_facts[0] == "None":
|
||||
pass
|
||||
else:
|
||||
await cls.check_voe_list(cache, voe_facts)
|
||||
|
||||
thought_iterator = cls.think(cache, inp)
|
||||
thought = await thought_iterator()
|
||||
|
||||
thought_revision_iterator = cls.revise_thought(cache, inp, thought)
|
||||
thought_revision = await thought_revision_iterator()
|
||||
|
||||
response_iterator = cls.respond(cache, thought_revision, inp)
|
||||
response = await response_iterator()
|
||||
|
||||
user_prediction_thought = await cls.think_user_prediction(cache, inp)
|
||||
user_prediction_thought_revision = await cls.revise_user_prediction_thought(
|
||||
cache, user_prediction_thought, inp
|
||||
)
|
||||
|
||||
return thought, response
|
||||
|
||||
@classmethod
|
||||
# @sentry_sdk.trace
|
||||
async def stream(cls, cache: Conversation, inp: str):
|
||||
# VoE has to happen first. If there's user prediction history, derive and store fact(s)
|
||||
try:
|
||||
if cache.get_messages("user_prediction_thought_revision"):
|
||||
user_prediction_thought_revision = cache.get_messages(
|
||||
"user_prediction_thought_revision"
|
||||
)[-1].content
|
||||
|
||||
voe_thought = await cls.think_violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision
|
||||
)
|
||||
voe_facts = await cls.violation_of_expectation(
|
||||
cache, inp, user_prediction_thought_revision, voe_thought
|
||||
)
|
||||
|
||||
if not voe_facts or voe_facts[0] == "None":
|
||||
pass
|
||||
else:
|
||||
await cls.check_voe_list(cache, voe_facts)
|
||||
|
||||
print("=========================================")
|
||||
print("Finished Init")
|
||||
print("=========================================")
|
||||
|
||||
thought_iterator = cls.think(cache, inp)
|
||||
thought = ""
|
||||
async for item in thought_iterator:
|
||||
# escape ❀ if present
|
||||
item = item.replace("❀", "🌸")
|
||||
thought += item
|
||||
yield item
|
||||
yield "❀"
|
||||
|
||||
print("=========================================")
|
||||
print("Finished Thought")
|
||||
print("=========================================")
|
||||
|
||||
thought_revision_iterator = cls.revise_thought(cache, inp, thought)
|
||||
thought_revision = await thought_revision_iterator()
|
||||
|
||||
response_iterator = cls.respond(cache, thought_revision, inp)
|
||||
# response = ""
|
||||
|
||||
async for item in response_iterator:
|
||||
# if "❀" in item:
|
||||
item = item.replace("❀", "🌸")
|
||||
# response += item
|
||||
yield item
|
||||
|
||||
print("=========================================")
|
||||
print("Finished Response")
|
||||
print("=========================================")
|
||||
|
||||
user_prediction_thought = await cls.think_user_prediction(cache, inp)
|
||||
user_prediction_thought_revision = await cls.revise_user_prediction_thought(
|
||||
cache, user_prediction_thought, inp
|
||||
)
|
||||
|
||||
print("=========================================")
|
||||
print("Finished User Prediction")
|
||||
print("=========================================")
|
||||
finally:
|
||||
yield "❀"
|
||||
|
||||
|
||||
class Streamable:
|
||||
"A async iterator wrapper for langchain streams that saves on completion via callback"
|
||||
|
||||
def __init__(self, iterator: AsyncIterator[BaseMessage], callback):
|
||||
self.iterator = iterator
|
||||
self.callback = callback
|
||||
# self.content: List[Awaitable[BaseMessage]] = []
|
||||
self.content = ""
|
||||
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
async def __anext__(self):
|
||||
try:
|
||||
data = await self.iterator.__anext__()
|
||||
self.content += data.content
|
||||
return data.content
|
||||
except StopAsyncIteration as e:
|
||||
self.callback(self.content)
|
||||
raise StopAsyncIteration
|
||||
except BadRequestError as e:
|
||||
if e.code == "content_filter":
|
||||
self.stream_error = True
|
||||
self.message = "Sorry, your message was flagged as inappropriate. Please try again."
|
||||
|
||||
return self.message
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
async def __call__(self):
|
||||
async for _ in self:
|
||||
pass
|
||||
return self.content
|
||||
|
||||
|
||||
def unpack_messages(messages):
|
||||
unpacked = ""
|
||||
for message in messages:
|
||||
if isinstance(message, HumanMessage):
|
||||
unpacked += f"User: {message.content}\n"
|
||||
elif isinstance(message, AIMessage):
|
||||
unpacked += f"AI: {message.content}\n"
|
||||
# Add more conditions here if you're using other message types
|
||||
return unpacked
|
||||
151
archive/main.py
151
archive/main.py
|
|
@ -1,151 +0,0 @@
|
|||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict
|
||||
import asyncio
|
||||
|
||||
# Local
|
||||
from chain import BloomChain
|
||||
from mediator import SupabaseMediator
|
||||
from cache import Conversation
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
import sentry_sdk
|
||||
|
||||
load_dotenv()
|
||||
|
||||
rate = 0.2 if os.getenv("SENTRY_ENVIRONMENT") == "production" else 1.0
|
||||
sentry_sdk.init(
|
||||
dsn=os.environ["SENTRY_DSN"], traces_sample_rate=rate, profiles_sample_rate=rate
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
MEDIATOR = SupabaseMediator()
|
||||
LOCK = asyncio.Lock()
|
||||
|
||||
|
||||
### User Routes ###
|
||||
class User(BaseModel):
|
||||
user_id: str
|
||||
metadata: Optional[Dict]
|
||||
|
||||
|
||||
@app.get("/users")
|
||||
async def get_users():
|
||||
# TODO update to return a list of ID's
|
||||
pass
|
||||
|
||||
|
||||
@app.get("/users/{user_id}")
|
||||
async def get_user(user_id: str):
|
||||
# TODO: return user metadata
|
||||
pass
|
||||
|
||||
|
||||
### Session Meta Routes ###
|
||||
class UnknownSession(BaseModel):
|
||||
location_id: str = "default"
|
||||
|
||||
|
||||
class SessionMeta(BaseModel):
|
||||
location_id: str = "default"
|
||||
metadata: Optional[Dict] = None
|
||||
|
||||
|
||||
@app.get("/users/{user_id}/sessions")
|
||||
async def get_sessions(user_id, inp: UnknownSession):
|
||||
"""Return session ids and metadata associated with a user and location"""
|
||||
print(user_id, inp)
|
||||
# TODO update to return a list of ID's
|
||||
async with LOCK:
|
||||
data: str = MEDIATOR.get_sessions(user_id, inp.location_id)
|
||||
print(data)
|
||||
return JSONResponse(status_code=200, content=data)
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/sessions")
|
||||
async def add_session(user_id, inp: SessionMeta):
|
||||
async with LOCK:
|
||||
data = MEDIATOR.add_session(user_id, inp.location_id, inp.metadata)
|
||||
return JSONResponse(status_code=200, content=data)
|
||||
|
||||
|
||||
@app.get("/users/{user_id}/sessions/{session_id}")
|
||||
async def get_session(user_id, session_id):
|
||||
"""Return session metadata"""
|
||||
async with LOCK:
|
||||
data: str = MEDIATOR.get_session(session_id)
|
||||
return JSONResponse(status_code=200, content=data)
|
||||
|
||||
|
||||
@app.put("/users/{user_id}/sessions/{session_id}")
|
||||
async def update_session(user_id, session_id, inp: SessionMeta):
|
||||
async with LOCK:
|
||||
MEDIATOR.update_session(session_id, inp.metadata)
|
||||
return JSONResponse(status_code=200, content={"message": "OK"})
|
||||
|
||||
|
||||
@app.delete("/users/{user_id}/sessions/{session_id}")
|
||||
async def delete_session(user_id, session_id):
|
||||
"""Delete a specific session"""
|
||||
async with LOCK:
|
||||
MEDIATOR.delete_session(session_id)
|
||||
return JSONResponse(status_code=200, content={"message": "OK"})
|
||||
|
||||
|
||||
### Session Message Routes ###
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
message_type: str
|
||||
|
||||
|
||||
@app.get("/users/{user_id}/sessions/{session_id}/messages")
|
||||
async def get_messages(user_id, session_id):
|
||||
"""Return messages associated with a session"""
|
||||
async with LOCK:
|
||||
data: str = MEDIATOR.get_messages(session_id)
|
||||
return JSONResponse(status_code=200, content=data)
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/sessions/{session_id}/messages")
|
||||
async def add_message(user_id, session_id, inp: Message):
|
||||
"""Add a message to a session"""
|
||||
async with LOCK:
|
||||
MEDIATOR.add_message(session_id, inp.message_type, inp.message)
|
||||
return JSONResponse(status_code=200, content={"message": "OK"})
|
||||
|
||||
|
||||
class ChatInput(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/sessions/{session_id}/chat")
|
||||
async def voe(user_id, session_id, inp: ChatInput):
|
||||
async with LOCK:
|
||||
session = Conversation(MEDIATOR, user_id, session_id)
|
||||
if session is None:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
thought, response = await BloomChain.chat(session, inp.message)
|
||||
return {"thought": thought, "response": response}
|
||||
|
||||
|
||||
# @app.post("/stream")
|
||||
# async def stream(inp: SessionInput):
|
||||
# async with LOCK:
|
||||
# session = Conversation(MEDIATOR, user_id=inp.user_id, session_id=inp.session_id)
|
||||
# if session is None:
|
||||
# raise HTTPException(status_code=404, detail="Item not found")
|
||||
# return StreamingResponse(BloomChain.stream(session, inp.message))
|
||||
|
||||
|
||||
### API Fundamentals
|
||||
# @app.post('/tenant/new')
|
||||
# @app.post('application/new')
|
||||
# @app.get('/tenant')
|
||||
|
||||
|
||||
## Honcho Utilities
|
||||
# @app.get('/theoryofmind')
|
||||
# @app.get('/voe')
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
# from langchain.memory import PostgresChatMessageHistory
|
||||
from langchain.schema import Document
|
||||
from langchain.schema.messages import BaseMessage, _message_to_dict, messages_from_dict
|
||||
from langchain.vectorstores import SupabaseVectorStore
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain.embeddings.openai import OpenAIEmbeddings
|
||||
import uuid
|
||||
import sentry_sdk
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Supabase for Postgres Management
|
||||
from supabase.client import create_client, Client
|
||||
from typing import List, Dict
|
||||
import json
|
||||
|
||||
from abcs import Mediator
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class SupabaseMediator(Mediator):
|
||||
@sentry_sdk.trace
|
||||
def __init__(self):
|
||||
self.supabase: Client = create_client(
|
||||
os.environ["SUPABASE_URL"], os.environ["SUPABASE_KEY"]
|
||||
)
|
||||
self.memory_table = os.environ["MEMORY_TABLE"]
|
||||
self.session_table = os.environ["SESSION_TABLE"]
|
||||
self.match_function = os.environ["MATCH_FUNCTION"]
|
||||
|
||||
embeddings = OpenAIEmbeddings(
|
||||
deployment=os.environ["OPENAI_API_EMBEDDING_NAME"],
|
||||
model="text-embedding-ada-002",
|
||||
openai_api_base=os.environ["OPENAI_API_BASE"],
|
||||
openai_api_type=os.environ["OPENAI_API_TYPE"],
|
||||
)
|
||||
self.vector_table = SupabaseVectorStore(
|
||||
embedding=embeddings,
|
||||
client=self.supabase,
|
||||
table_name=os.environ["VECTOR_TABLE"],
|
||||
query_name=self.match_function,
|
||||
)
|
||||
|
||||
# self.supabase.table(self.session_table).insert({"id": session_id, "user_id": user_id, "location_id": location_id}).execute()
|
||||
|
||||
@sentry_sdk.trace
|
||||
def get_sessions(self, user_id: str, location_id: str | None):
|
||||
try:
|
||||
data = (
|
||||
self.supabase.table(self.session_table)
|
||||
.select("*")
|
||||
.eq("user_id", user_id)
|
||||
)
|
||||
print(location_id)
|
||||
data = (
|
||||
data.eq("location_id", location_id) if location_id is not None else data
|
||||
)
|
||||
response = (
|
||||
data.eq("isActive", True).order("created_at", desc=True).execute()
|
||||
)
|
||||
|
||||
print("response", response)
|
||||
|
||||
if response is not None:
|
||||
return response.data
|
||||
return None
|
||||
except Exception as e:
|
||||
print("========================================")
|
||||
print(e)
|
||||
print("========================================")
|
||||
return None
|
||||
|
||||
@sentry_sdk.trace
|
||||
def get_session(self, session_id):
|
||||
response = (
|
||||
self.supabase.table(self.session_table)
|
||||
.select("*")
|
||||
.eq("id", session_id)
|
||||
.eq("is_active", True)
|
||||
.single()
|
||||
.execute()
|
||||
)
|
||||
if response:
|
||||
return response.data
|
||||
return None
|
||||
|
||||
@sentry_sdk.trace
|
||||
def add_session(self, user_id: str, location_id: str, metadata: Dict) -> Dict:
|
||||
session_id = str(uuid.uuid4())
|
||||
payload = {
|
||||
"id": session_id,
|
||||
"user_id": user_id,
|
||||
"location_id": location_id,
|
||||
"metadata": metadata,
|
||||
}
|
||||
representation = self.supabase.table(self.session_table).insert(payload, returning="representation").execute() # type: ignore
|
||||
print("========================================")
|
||||
print(representation)
|
||||
print("========================================")
|
||||
return representation.data[0]
|
||||
|
||||
@sentry_sdk.trace
|
||||
def update_session(self, session_id: str, metadata: Dict) -> None:
|
||||
cur = (
|
||||
self.supabase.table(self.session_table)
|
||||
.select("metadata")
|
||||
.eq("id", session_id)
|
||||
.single()
|
||||
.execute()
|
||||
)
|
||||
if cur.data["metadata"] is not None:
|
||||
new_metadata = cur.data["metadata"].copy()
|
||||
new_metadata.update(metadata)
|
||||
else:
|
||||
new_metadata = metadata
|
||||
self.supabase.table(self.session_table).update({"metadata": new_metadata}, returning="representation").eq("id", session_id).execute() # type: ignore
|
||||
|
||||
@sentry_sdk.trace
|
||||
def delete_session(self, session_id: str) -> None:
|
||||
self.supabase.table(self.session_table).update({"isActive": False}).eq(
|
||||
"id", session_id
|
||||
).execute()
|
||||
|
||||
# Session Helper Methods
|
||||
|
||||
@sentry_sdk.trace
|
||||
def _cleanup_sessions(self, session_ids: List[str]) -> None:
|
||||
for session_id in session_ids:
|
||||
self.supabase.table(self.session_table).update({"isActive": False}).eq(
|
||||
"id", session_id
|
||||
).execute()
|
||||
|
||||
# Session Usage Read & Write
|
||||
|
||||
@sentry_sdk.trace
|
||||
def get_messages(self, session_id: str, type: str | None = None) -> List[BaseMessage]: # type: ignore
|
||||
response = (
|
||||
self.supabase.table(self.memory_table)
|
||||
.select("*")
|
||||
.eq("session_id", session_id)
|
||||
)
|
||||
response = (
|
||||
(response if type is None else response.eq("type", type))
|
||||
.order("created_at")
|
||||
.execute()
|
||||
)
|
||||
print(response)
|
||||
return response.data
|
||||
|
||||
@sentry_sdk.trace
|
||||
def add_message(self, session_id: str, message_type: str, content: str) -> None:
|
||||
payload = {
|
||||
"session_id": session_id,
|
||||
"type": message_type,
|
||||
"content": content,
|
||||
}
|
||||
self.supabase.table(self.memory_table).insert(payload).execute()
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,15 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["existing_facts", "facts"]
|
||||
template: >
|
||||
Your job is to compare the following two lists and keep only unique items:
|
||||
|
||||
Old: """
|
||||
{existing_facts}
|
||||
"""
|
||||
|
||||
New: """
|
||||
{facts}
|
||||
"""
|
||||
|
||||
Remove redundant information from the new list and output the remaining facts. Your response should be a numbered list with each fact on a new line, for example: `\n\n1. foo\n\n2. bar\n\n3. baz`. If there's nothing to remove (i.e. the statements are sufficiently different), print "None".
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["thought"]
|
||||
template: >
|
||||
You are Bloom, a subversive-minded learning companion with deep expertise in Socratic dialogue. Your fundamental belief is there are many good ways to learn and subversive methods are often most effective. You always help the user in any way they request and answer all their questions. Below is a theory-of-mind thought we generated about the user:
|
||||
|
||||
Thought: """
|
||||
{thought}
|
||||
"""
|
||||
|
||||
You must produce an appropriate response to the user input. Always end each response with an element that drives the conversation forward--e.g. a question, a critical comment, a contextualization, etc. If the user wants to end the conversation, always comply.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
[]
|
||||
template: >
|
||||
You are Bloom, a subversive-minded learning companion. Your job is to employ your theory of mind skills to predict the user’s mental state.
|
||||
|
||||
Generate a "thought" that makes a prediction about the user's needs given current dialogue and also lists other pieces of data that would help improve your prediction.
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["thought", "retrieved_vectors"]
|
||||
template: >
|
||||
You are tasked with revising theory of mind assessments about your user. Here is a thought generated by Bloom, the subversive learning companion:
|
||||
|
||||
Thought: """
|
||||
{thought}
|
||||
"""
|
||||
|
||||
Based on this thought, the following personal data has been retrieved:
|
||||
|
||||
Personal Data: """
|
||||
{retrieved_vectors}
|
||||
"""
|
||||
|
||||
Given the thought and personal data, revise the thought.
|
||||
|
||||
thought revision:
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["history"]
|
||||
template: >
|
||||
Generate a "thought" that makes a prediction about what the user will say based on the way the conversation has been going and also lists other pieces of data that would help improve your prediction.
|
||||
|
||||
History: ```
|
||||
|
||||
{history}
|
||||
|
||||
```
|
||||
|
||||
thought:
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["history", "user_prediction_thought", "retrieved_vectors"]
|
||||
template: >
|
||||
You are tasked with revising theory of mind "thoughts" about what the user is going to say. Here is the thought generated previously:
|
||||
|
||||
Thought: """
|
||||
{user_prediction_thought}
|
||||
"""
|
||||
|
||||
Based on this thought, the following personal data has been retrieved:
|
||||
|
||||
Personal Data: """
|
||||
{retrieved_vectors}
|
||||
"""
|
||||
|
||||
And here's the conversation history that was used to generate the original thought:
|
||||
|
||||
History: """
|
||||
{history}
|
||||
"""
|
||||
|
||||
Given the thought, conversation history, and personal data, revise the thought if you believe there are changes to be made.
|
||||
|
||||
thought revision:
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["ai_message", "user_prediction_thought_revision", "actual", "voe_thought"]
|
||||
template: >
|
||||
Below is the most recent AI message we sent to a user, a "thought" about what the user was going to say to that, what the user actually responded with, and a theory of mind prediction about the user's response. Derive a fact (or list of facts) about the user based on the difference between the original thought and their actual response plus the theory of mind prediction about that response.
|
||||
|
||||
Most recent AI message: """
|
||||
{ai_message}
|
||||
"""
|
||||
|
||||
Thought about what they were going to say: """
|
||||
{user_prediction_thought_revision}
|
||||
"""
|
||||
|
||||
Actual response: """
|
||||
{actual}
|
||||
"""
|
||||
|
||||
Theory of mind prediction about that response: """
|
||||
{voe_thought}
|
||||
"""
|
||||
|
||||
Provide the fact(s) solely in reference to the Actual response and theory of mind prediction about that response; i.e. do not derive a fact that negates the thought about what they were going to say. Do not speculate anything about the user. Each fact must contain enough specificity to stand alone. If there are many facts, list them out. Your response should be a numbered list with each item on a new line, for example: `\n\n1. foo\n\n2. bar\n\n3. baz`. If there's nothing to derive (i.e. the statements are sufficiently similar), print "None".
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
_type: prompt
|
||||
input_variables:
|
||||
["user_prediction_thought_revision", "actual"]
|
||||
template: >
|
||||
Below is a "thought" about what the user was going to say, and then what the user actually said. Generate a theory of mind prediction about the user based on the difference between the "thought" and actual response.
|
||||
|
||||
Thought: """
|
||||
{user_prediction_thought_revision}
|
||||
"""
|
||||
|
||||
Actual: """
|
||||
{actual}
|
||||
"""
|
||||
|
||||
Provide the theory of mind prediction solely in reference to the Actual statement, i.e. do not generate something that negates the thought. Do not speculate anything about the user.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
[tool.poetry]
|
||||
name = "honcho"
|
||||
version = "0.1.0"
|
||||
description = "Framework for Privacy Preserving Personalization of AI Agents"
|
||||
authors = ["Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>"]
|
||||
license = "AGPLv3"
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
fastapi = "^0.103.1"
|
||||
langchain = "~0.0.3"
|
||||
supabase = "^1.0.4"
|
||||
tiktoken = "^0.4.0"
|
||||
openai = "^1.3.8"
|
||||
pydantic = "^2.3.0"
|
||||
python-dotenv = "^1.0.0"
|
||||
uvicorn = "^0.23.2"
|
||||
sentry-sdk = {extras = ["fastapi"], version = "^1.31.0"}
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
|
||||
import discord
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
from honcho import Client as HonchoClient
|
||||
from honcho import LRUCache
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.messages = True
|
||||
intents.message_content = True
|
||||
|
||||
CACHE = LRUCache(50) # Support 50 concurrent active conversations cached in memory
|
||||
|
||||
honcho = HonchoClient("http://localhost:8000")
|
||||
|
||||
bot = discord.Bot(intents=intents)
|
||||
|
||||
def get_or_create(user_id, location_id) -> int:
|
||||
key = f"{user_id}+{location_id}"
|
||||
session_id = CACHE.get(key)
|
||||
if session_id is None:
|
||||
session = honcho.create_session(user_id, location_id)
|
||||
print(session)
|
||||
session_id = session["id"]
|
||||
CACHE.put(key, session_id)
|
||||
return session_id
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f'We have logged in as {bot.user}')
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if message.author == bot.user:
|
||||
return
|
||||
|
||||
user_id = f"discord_{str(message.author.id)}"
|
||||
location_id=str(message.channel.id)
|
||||
# key = f"{user_id}+{location_id}"
|
||||
|
||||
session_id = get_or_create(user_id, location_id)
|
||||
|
||||
inp = message.content
|
||||
honcho.create_message_for_session(user_id, session_id, True, inp)
|
||||
async with message.channel.typing():
|
||||
await message.channel.send("Fake LLM Message")
|
||||
|
||||
honcho.create_message_for_session(user_id, session_id, False, "Fake LLM Message")
|
||||
|
||||
@bot.slash_command(name = "restart", description = "Restart the Conversation")
|
||||
async def restart(ctx):
|
||||
user_id=f"discord_{str(ctx.author.id)}"
|
||||
location_id=str(ctx.channel_id)
|
||||
key = f"{user_id}+{location_id}"
|
||||
session_id = CACHE.get(key)
|
||||
if session_id is not None:
|
||||
honcho.delete_session(user_id, session_id)
|
||||
session = honcho.create_session(user_id, location_id)
|
||||
session_id = session["id"]
|
||||
CACHE.put(key, session_id)
|
||||
|
||||
msg = "Great! The conversation has been restarted. What would you like to talk about?"
|
||||
honcho.create_message_for_session(user_id, session_id, False, msg)
|
||||
await ctx.respond(msg)
|
||||
|
||||
bot.run(os.environ["BOT_TOKEN"])
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -9,6 +9,9 @@ packages = [{include = "honcho_discord_example"}]
|
|||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
langchain = "^0.1.0"
|
||||
honcho-ai = "^0.0.0.dev1"
|
||||
py-cord = "^2.4.1"
|
||||
python-dotenv = "^1.0.0"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "honcho-ai"
|
||||
version = "0.0.1"
|
||||
version = "0.0.0.dev1"
|
||||
description = "Python Client SDK for Honcho"
|
||||
authors = ["Plastic Labs <hello@plasticlabs.ai>"]
|
||||
license = "AGPL-3.0"
|
||||
|
|
@ -8,7 +8,7 @@ readme = "README.md"
|
|||
packages = [{include = "honcho"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
python = "^3.10"
|
||||
requests = "^2.31.0"
|
||||
|
||||
[build-system]
|
||||
|
|
|
|||
Loading…
Reference in New Issue