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

v0.0.8 release #53

Merged
merged 5 commits into from
May 9, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Honcho is a platform for making AI agents and LLM powered applications that are personalized
to their end users.

Read about the motivation of this project [here](https://blog.plasticlabs.ai).
Read about the motivation of this project [here](https://blog.plasticlabs.ai/blog/A-Simple-Honcho-Primer).

Read the user documenation [here](https://docs.honcho.dev)

Expand Down
96 changes: 95 additions & 1 deletion api/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ psycopg = {extras = ["binary"], version = "^3.1.18"}
langchain = "^0.1.12"
langchain-openai = "^0.0.8"
httpx = "^0.27.0"
uvloop = "^0.19.0"
httptools = "^0.6.1"

[tool.ruff.lint]
# from https://docs.astral.sh/ruff/linter/#rule-selection example
Expand Down
39 changes: 31 additions & 8 deletions api/src/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
llm: ChatOpenAI = ChatOpenAI(model_name="gpt-4")


async def chat(
async def prep_inference(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
query: str,
db: AsyncSession,
):
collection = await crud.get_collection_by_name(db, app_id, user_id, "honcho")
retrieved_facts = None
Expand All @@ -58,6 +58,19 @@ async def chat(

dialectic_prompt = ChatPromptTemplate.from_messages([system_dialectic])
chain = dialectic_prompt | llm
return (chain, retrieved_facts)


async def chat(
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
query: str,
db: AsyncSession,
):
(chain, retrieved_facts) = await prep_inference(
db, app_id, user_id, session_id, query
)
response = await chain.ainvoke(
{
"agent_input": query,
Expand All @@ -68,9 +81,19 @@ async def chat(
return schemas.AgentChat(content=response.content)


async def hydrate():
pass


async def insight():
pass
async def stream(
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
query: str,
db: AsyncSession,
):
(chain, retrieved_facts) = await prep_inference(
db, app_id, user_id, session_id, query
)
return chain.astream(
{
"agent_input": query,
"retrieved_facts": retrieved_facts if retrieved_facts else "None",
}
)
2 changes: 1 addition & 1 deletion api/src/deriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
SUPABASE_ID = os.getenv("SUPABASE_ID")
SUPABASE_API_KEY = os.getenv("SUPABASE_API_KEY")

llm = ChatOpenAI(model_name="gpt-3.5")
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
output_parser = NumberedListOutputParser()

SYSTEM_DERIVE_FACTS = load_prompt(
Expand Down
98 changes: 35 additions & 63 deletions api/src/main.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import json
import logging
import os
import re
import uuid
from contextlib import asynccontextmanager
from typing import Optional, Sequence

import httpx
import sentry_sdk
from fastapi import (
APIRouter,
FastAPI,
Request,
)
from fastapi import APIRouter, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import PlainTextResponse
from fastapi_pagination import add_pagination
from opentelemetry import trace
Expand Down Expand Up @@ -46,8 +39,6 @@
from slowapi.middleware import SlowAPIMiddleware
from slowapi.util import get_remote_address
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response

from src.routers import (
apps,
Expand Down Expand Up @@ -199,12 +190,43 @@ async def lifespan(app: FastAPI):
await engine.dispose()


app = FastAPI(lifespan=lifespan)
app = FastAPI(
lifespan=lifespan,
servers=[
{"url": "http://127.0.0.1:8000", "description": "Local Development Server"},
{"url": "https:/demo.honcho.dev", "description": "Demo Server"},
],
title="Honcho API",
summary="An API for adding personalization to AI Apps",
description="""This API is used to store data and get insights about users for AI
applications""",
version="0.1.0",
contact={
"name": "Plastic Labs",
"url": "https://plasticlabs.ai",
"email": "[email protected]",
},
license_info={
"name": "GNU Affero General Public License v3.0",
"identifier": "AGPL-3.0-only",
"url": "https://github.com/plastic-labs/honcho/blob/main/LICENSE",
},
)

origins = ["http://localhost", "http://127.0.0.1:8000", "https://demo.honcho.dev"]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)


if OPENTELEMTRY_ENABLED:
FastAPIInstrumentor().instrument_app(app)


router = APIRouter(prefix="/apps/{app_id}/users/{user_id}")

# Create a Limiter instance
Expand All @@ -221,56 +243,6 @@ async def lifespan(app: FastAPI):

add_pagination(app)

USE_AUTH_SERVICE = os.getenv("USE_AUTH_SERVICE", "False").lower() == "true"
AUTH_SERVICE_URL = os.getenv("AUTH_SERVICE_URL", "http://localhost:8001")


class BearerTokenMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
authorization: Optional[str] = request.headers.get("Authorization")
if authorization:
scheme, _, token = authorization.partition(" ")
if scheme.lower() == "bearer" and token:
id_pattern = r"\/apps\/([^\/]+)"
name_pattern = r"\/apps\/name\/([^\/]+)|\/apps\/get_or_create\/([^\/]+)"
match_id = re.search(id_pattern, request.url.path)
match_name = re.search(name_pattern, request.url.path)
payload = {"token": token}
if match_name:
payload["name"] = match_name.group(1)
elif match_id:
payload["app_id"] = match_id.group(1)

res = httpx.get(
f"{AUTH_SERVICE_URL}/validate",
params=payload,
)
data = res.json()
if (
data["app_id"] or data["name"]
): # Anything that checks app_id if True is valid
return await call_next(request)
if data["token"]:
check_pattern = r"^\/apps$|^\/apps\/get_or_create"
match = re.search(check_pattern, request.url.path)
if match:
return await call_next(request)

return Response(content="Invalid token.", status_code=400)
else:
return Response(
content="Invalid authentication scheme.", status_code=400
)

exclude_paths = ["/docs", "/redoc", "/openapi.json"]
if request.url.path in exclude_paths:
return await call_next(request)
return Response(content="Authorization header missing.", status_code=401)


if USE_AUTH_SERVICE:
app.add_middleware(BearerTokenMiddleware)


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
Expand Down
Loading
Loading