-
Notifications
You must be signed in to change notification settings - Fork 13
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
Adds export
functionality to chat, removes poetry in favor of uv, bumps Honcho version to v0.0.14/python-SDK-0.0.19
#15
base: main
Are you sure you want to change the base?
Changes from 4 commits
b908a3b
f990741
1104be1
b678112
9756163
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,30 +2,24 @@ | |
# https://testdriven.io/blog/docker-best-practices/ | ||
FROM python:3.11-slim-bullseye | ||
|
||
RUN apt-get update && apt-get install -y build-essential | ||
RUN apt-get update && apt-get install -y build-essential curl | ||
|
||
WORKDIR /app | ||
|
||
# https://stackoverflow.com/questions/53835198/integrating-python-poetry-with-docker | ||
# Set Python environment variables and default port | ||
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.8.3 | ||
PYTHONUNBUFFERED=1 \ | ||
PYTHONHASHSEED=random \ | ||
PORT=8000 | ||
|
||
RUN pip install "poetry==$POETRY_VERSION" | ||
# Install uv and add to PATH | ||
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ | ||
mv /root/.local/bin/uv /usr/local/bin/ && \ | ||
mv /root/.local/bin/uvx /usr/local/bin/ | ||
|
||
# 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 | ||
|
||
WORKDIR /app | ||
# Copy requirements and install dependencies | ||
COPY requirements.txt . | ||
RUN uv pip install --system -r requirements.txt | ||
|
||
RUN addgroup --system app && adduser --system --group app | ||
RUN chown -R app:app /app | ||
|
@@ -34,7 +28,5 @@ USER app | |
COPY --chown=app:app app.py /app/app.py | ||
COPY --chown=app:app calls.py /app/calls.py | ||
|
||
EXPOSE 8000 | ||
|
||
# https://stackoverflow.com/questions/29663459/python-app-does-not-print-anything-when-running-detached-in-docker | ||
CMD ["fastapi", "run", "app.py", "--host", "0.0.0.0", "--port", "8000"] | ||
EXPOSE ${PORT} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. internal port doesn't matter we can always change what port we expose on the outside. should just keep this to static port |
||
CMD uvicorn app:app --host 0.0.0.0 --port ${PORT} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,30 @@ | ||
import os | ||
from contextvars import ContextVar | ||
from typing import Annotated, Any, Dict | ||
from typing import Annotated, Any, Dict, List, Literal | ||
from functools import cache | ||
from fastapi.security import OAuth2PasswordBearer | ||
from fastapi.responses import HTMLResponse, FileResponse | ||
from fastapi.staticfiles import StaticFiles | ||
|
||
import sentry_sdk | ||
from fastapi import Depends, FastAPI, HTTPException, status | ||
from starlette.middleware.cors import CORSMiddleware | ||
from fastapi.responses import StreamingResponse | ||
from fastapi.responses import StreamingResponse, FileResponse | ||
from honcho import Honcho | ||
from pydantic import BaseModel | ||
|
||
from cryptography.fernet import Fernet | ||
import base64 | ||
|
||
from calls import GaslitClaude, Simulator | ||
|
||
import jwt | ||
|
||
|
||
from dotenv import load_dotenv | ||
|
||
import tempfile | ||
import json | ||
from datetime import datetime | ||
|
||
load_dotenv(override=True) | ||
|
||
|
||
|
@@ -35,13 +39,23 @@ def get_env(key: str): | |
CLIENT_REGEX = get_env("CLIENT_REGEX") | ||
print(CLIENT_REGEX) | ||
JWT_SECRET = get_env("JWT_SECRET") | ||
SECRET_KEY = base64.b64decode(get_env("SECRET_KEY")) | ||
SECRET_KEY = get_env("SECRET_KEY").encode() | ||
HONCHO_APP_NAME = get_env("HONCHO_APP_NAME") | ||
|
||
fernet = Fernet(SECRET_KEY) | ||
|
||
honcho = Honcho(base_url=HONCHO_ENV) | ||
honcho_app = honcho.apps.get_or_create(HONCHO_APP_NAME) | ||
print(f"Initializing Honcho with base_url: {HONCHO_ENV}") | ||
honcho = Honcho( | ||
base_url=HONCHO_ENV, | ||
) | ||
|
||
try: | ||
print(f"Attempting to get/create app: {HONCHO_APP_NAME}") | ||
honcho_app = honcho.apps.get_or_create(HONCHO_APP_NAME) | ||
print(f"Successfully initialized app with id: {honcho_app.id}") | ||
except Exception as e: | ||
print(f"Error initializing Honcho app: {str(e)}") | ||
raise | ||
|
||
|
||
gaslit_ctx = ContextVar( | ||
|
@@ -72,7 +86,6 @@ def get_env(key: str): | |
allow_headers=["*"], | ||
) | ||
|
||
|
||
class BaseRequest(BaseModel): | ||
session_id: str | ||
|
||
|
@@ -212,10 +225,24 @@ async def reset( | |
honcho.apps.users.sessions.delete( | ||
app_id=honcho_app.id, session_id=session_id, user_id=user_id | ||
) | ||
session = honcho.apps.users.sessions.create(app_id=honcho_app.id, user_id=user_id) | ||
# TODO reset the session | ||
# gaslit_claude.history = [] | ||
# simulator.history = [] | ||
try: | ||
session = honcho.apps.users.sessions.create( | ||
app_id=honcho_app.id, | ||
user_id=user_id, | ||
) | ||
except TypeError as e: | ||
if "location_id" in str(e): | ||
# If location_id is truly optional, try without it | ||
session = honcho.apps.users.sessions.create( | ||
app_id=honcho_app.id, | ||
user_id=user_id | ||
) | ||
else: | ||
raise e | ||
|
||
Comment on lines
+269
to
+283
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this for backwards compatability? |
||
return { | ||
"user_id": user_id, | ||
"session_id": session.id, | ||
|
@@ -299,3 +326,44 @@ async def share_messages(code: str): | |
return await get_session_messages(session_id=session_id, user_id=user_id) | ||
except Exception: | ||
raise HTTPException(status_code=400, detail="Invalid encrypted data") | ||
|
||
class MessageFormat(BaseModel): | ||
role: Literal["user", "assistant"] | ||
content: str | ||
|
||
@app.get("/export/{session_id}") | ||
async def export_session(session_id: str, user_id: str = Depends(get_current_user)): | ||
try: | ||
messages = honcho.apps.users.sessions.messages.list( | ||
app_id=honcho_app.id, | ||
user_id=user_id, | ||
session_id=session_id | ||
) | ||
|
||
formatted_messages = [ | ||
{ | ||
"role": "user" if msg.is_user else "assistant", | ||
"content": msg.content | ||
} for msg in messages | ||
] | ||
|
||
# Create a temporary file | ||
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as tmp: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is writing to disk necessary here? i feel like this could be done in memeory There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or is this already in memory? i've never used this library before, if so, neat trick. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From python docs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Output from claude about it. Maybe we should used The Python tempfile library provides both in-memory and on-disk temporary storage options. Let me explain the key functionality: For on-disk temporary files and directories:
For in-memory temporary storage:
Here's a practical example: import tempfile
# On-disk temporary file
with tempfile.NamedTemporaryFile() as tf:
tf.write(b"Some data")
tf.seek(0)
print(tf.read()) # File is automatically deleted after this block
# In-memory with potential disk spillover
with tempfile.SpooledTemporaryFile(max_size=1024) as tf:
tf.write(b"In memory until exceeding 1KB")
tf.seek(0)
print(tf.read()) The temporary files are created in your system's default temporary directory (usually /tmp on Unix-like systems or the user's temp directory on Windows). This location can be customized using the Would you like me to explain any specific aspect in more detail? |
||
json.dump(formatted_messages, tmp, indent=2) | ||
tmp_path = tmp.name | ||
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | ||
filename = f"yousim_conversation_{timestamp}.json" | ||
|
||
return FileResponse( | ||
path=tmp_path, | ||
filename=filename, | ||
media_type='application/json', | ||
background=None # Ensures file is sent before deletion | ||
) | ||
|
||
except Exception as e: | ||
raise HTTPException( | ||
status_code=400, | ||
detail=f"Failed to export session: {str(e)}" | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,15 @@ | ||
version: '3.8' | ||
# version: '3.8' | ||
|
||
services: | ||
backend: | ||
build: | ||
context: . | ||
dockerfile: Dockerfile | ||
ports: | ||
- "8000:8000" | ||
- "${PORT}:${PORT}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we care about the port within the container? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made this more complicated than needed because I was running my There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should just stick to default setup in the compose. Can change as we need in local development, but shouldn't push it up. |
||
environment: | ||
- HONCHO_AUTH_TOKEN=default | ||
- PORT=${PORT} | ||
env_file: | ||
- .env | ||
|
||
|
@@ -18,3 +21,5 @@ services: | |
- "3000:3000" | ||
depends_on: | ||
- backend | ||
env_file: | ||
- ./webshell/.env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be a
uv sync
command. Should follow conventions from honcho repo or on https://docs.astral.sh/uv/guides/integration/docker/