agent / chat backend
This commit is contained in:
parent
f1c1d63ed9
commit
8d9f559a64
4 changed files with 68 additions and 17 deletions
|
|
@ -5,7 +5,7 @@ description = "Add your description here"
|
|||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"agentd>=0.1.7",
|
||||
"agentd>=0.1.8",
|
||||
"aiofiles>=24.1.0",
|
||||
"docling>=2.41.0",
|
||||
"opensearch-py[async]>=3.0.0",
|
||||
|
|
|
|||
23
src/agent.py
Normal file
23
src/agent.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import asyncio
|
||||
|
||||
from agentd.patch import patch_openai_with_mcp
|
||||
|
||||
messages = [{"role": "system", "content": "You are a helpful assistant. use your tools to answer questions."}]
|
||||
|
||||
# Async version for web server
|
||||
async def async_chat(async_client, prompt: str) -> str:
|
||||
global messages
|
||||
messages += [{"role": "user", "content": prompt}]
|
||||
response = await async_client.chat.completions.create(
|
||||
model="gpt-4.1-mini",
|
||||
messages=messages,
|
||||
mcp_strict=True
|
||||
)
|
||||
|
||||
response_text = response.choices[0].message.content
|
||||
print(f"user ==> {prompt}")
|
||||
print(f"agent ==> {response_text}")
|
||||
return response_text
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(async_chat("What pods are there?"))
|
||||
52
src/app.py
52
src/app.py
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from typing import Any
|
||||
|
||||
from agent import async_chat
|
||||
|
||||
os.environ['USE_CPU_ONLY'] = 'true'
|
||||
|
||||
|
|
@ -20,7 +23,8 @@ from opensearchpy import AsyncOpenSearch
|
|||
from opensearchpy._async.http_aiohttp import AIOHttpConnection
|
||||
from docling.document_converter import DocumentConverter
|
||||
from agentd.patch import patch_openai_with_mcp
|
||||
from openai import OpenAI
|
||||
from openai import AsyncOpenAI
|
||||
from agentd.tool_decorator import tool
|
||||
|
||||
# Initialize Docling converter
|
||||
converter = DocumentConverter() # basic converter; tweak via PipelineOptions if you need OCR, etc.
|
||||
|
|
@ -75,7 +79,7 @@ index_body = {
|
|||
}
|
||||
}
|
||||
|
||||
client = patch_openai_with_mcp(OpenAI()) # Get the patched client back
|
||||
async_client = patch_openai_with_mcp(AsyncOpenAI()) # Get the patched client back
|
||||
|
||||
async def wait_for_opensearch():
|
||||
"""Wait for OpenSearch to be ready with retries"""
|
||||
|
|
@ -170,7 +174,7 @@ async def process_file_common(file_path: str, file_hash: str = None):
|
|||
slim_doc = extract_relevant(full_doc)
|
||||
|
||||
texts = [c["text"] for c in slim_doc["chunks"]]
|
||||
resp = client.embeddings.create(model=EMBED_MODEL, input=texts)
|
||||
resp = await async_client.embeddings.create(model=EMBED_MODEL, input=texts)
|
||||
embeddings = [d.embedding for d in resp.data]
|
||||
|
||||
# Index each chunk as a separate document
|
||||
|
|
@ -236,15 +240,31 @@ async def upload_path(request: Request):
|
|||
return JSONResponse({"results": results})
|
||||
|
||||
async def search(request: Request):
|
||||
|
||||
payload = await request.json()
|
||||
query = payload.get("query")
|
||||
if not query:
|
||||
return JSONResponse({"error": "Query is required"}, status_code=400)
|
||||
return JSONResponse(await search_tool(query))
|
||||
|
||||
|
||||
@tool
|
||||
async def search_tool(query: str)-> dict[str, Any]:
|
||||
"""
|
||||
Use this tool to search for documents relevant to the query.
|
||||
|
||||
This endpoint accepts POST requests with a query string,
|
||||
|
||||
Args:
|
||||
query (str): query string to search the corpus
|
||||
|
||||
Returns:
|
||||
dict (str, Any)
|
||||
- {"results": [chunks]} on success
|
||||
"""
|
||||
# Embed the query
|
||||
resp = client.embeddings.create(model=EMBED_MODEL, input=[query])
|
||||
resp = await async_client.embeddings.create(model=EMBED_MODEL, input=[query])
|
||||
query_embedding = resp.data[0].embedding
|
||||
|
||||
# Search using vector similarity on individual chunks
|
||||
search_body = {
|
||||
"query": {
|
||||
|
|
@ -258,9 +278,7 @@ async def search(request: Request):
|
|||
"_source": ["filename", "mimetype", "page", "text"],
|
||||
"size": 10
|
||||
}
|
||||
|
||||
results = await es.search(index=INDEX_NAME, body=search_body)
|
||||
|
||||
# Transform results to match expected format
|
||||
chunks = []
|
||||
for hit in results["hits"]["hits"]:
|
||||
|
|
@ -271,13 +289,23 @@ async def search(request: Request):
|
|||
"text": hit["_source"]["text"],
|
||||
"score": hit["_score"]
|
||||
})
|
||||
|
||||
return JSONResponse({"results": chunks})
|
||||
return {"results": chunks}
|
||||
|
||||
async def chat_endpoint(request):
|
||||
data = await request.json()
|
||||
prompt = data.get("prompt", "")
|
||||
|
||||
if not prompt:
|
||||
return JSONResponse({"error": "Prompt is required"}, status_code=400)
|
||||
|
||||
response = await async_chat(async_client, prompt)
|
||||
return JSONResponse({"response": response})
|
||||
|
||||
app = Starlette(debug=True, routes=[
|
||||
Route("/upload", upload, methods=["POST"]),
|
||||
Route("/upload_path", upload_path, methods=["POST"]),
|
||||
Route("/search", search, methods=["POST"]),
|
||||
Route("/upload", upload, methods=["POST"]),
|
||||
Route("/upload_path", upload_path, methods=["POST"]),
|
||||
Route("/search", search, methods=["POST"]),
|
||||
Route("/chat", chat_endpoint, methods=["POST"]),
|
||||
])
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
8
uv.lock
generated
8
uv.lock
generated
|
|
@ -9,7 +9,7 @@ resolution-markers = [
|
|||
|
||||
[[package]]
|
||||
name = "agentd"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "litellm" },
|
||||
|
|
@ -18,9 +18,9 @@ dependencies = [
|
|||
{ name = "openai-agents" },
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/f6/6fc7c171e0c874e816ec063bf3cbabda4f05c6f4f1f15b65955255951027/agentd-0.1.7.tar.gz", hash = "sha256:cdcbf56dfdc0ca56067f354d890841bd71ee52210c12e468e711554007a6724c", size = 114216, upload-time = "2025-06-05T04:50:52.693Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ea/e1/a3d0d2ddb6639be34d906f13a2edc91dbf174f8dcf97a68705f3a613ff8d/agentd-0.1.8.tar.gz", hash = "sha256:9278916d228d23d67283aed0e420d14f3b6862499df5cc5a8adb92ab3583ed17", size = 114252, upload-time = "2025-07-11T16:06:57.478Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/c7/e786759ebd86afba150ccc1e8189e8515c760445f04374e2c4d72701c2aa/agentd-0.1.7-py3-none-any.whl", hash = "sha256:39332b8cf57dfc14b48628dfeb588afb6dbe05405945389fbe2925f3358b7c65", size = 13446, upload-time = "2025-06-05T04:50:51.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/ca/9caa1253ab3ba151e725ea02aba334c29d659568b5341e5d886dbb394d85/agentd-0.1.8-py3-none-any.whl", hash = "sha256:15cc05ccbedfa9df8983a7a67c274c0c5a7ef029e55e6c0d7639106022c5cf06", size = 13472, upload-time = "2025-07-11T16:06:56.014Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -434,7 +434,7 @@ dependencies = [
|
|||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "agentd", specifier = ">=0.1.7" },
|
||||
{ name = "agentd", specifier = ">=0.1.8" },
|
||||
{ name = "aiofiles", specifier = ">=24.1.0" },
|
||||
{ name = "docling", specifier = ">=2.41.0" },
|
||||
{ name = "opensearch-py", extras = ["async"], specifier = ">=3.0.0" },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue