Spaces:
Sleeping
Sleeping
update app.py and requirements.txt files
Browse files- app.py +48 -38
- requirements.txt +0 -1
app.py
CHANGED
|
@@ -1,64 +1,74 @@
|
|
| 1 |
-
|
| 2 |
from dataclasses import dataclass
|
| 3 |
-
import
|
| 4 |
-
from pydantic import BaseModel
|
| 5 |
from pydantic_ai import Agent, RunContext
|
| 6 |
import gradio as gr
|
| 7 |
import os
|
|
|
|
| 8 |
|
|
|
|
| 9 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 10 |
if not OPENAI_API_KEY:
|
| 11 |
raise RuntimeError("Missing OPENAI_API_KEY. Add it in Spaces → Settings → Secrets.")
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
class Review(BaseModel):
|
| 14 |
why: str
|
| 15 |
-
rating: int
|
| 16 |
recommended: bool
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
| 21 |
|
| 22 |
async def get_movie_info(self, title: str) -> str:
|
| 23 |
title = (title or "").strip()
|
| 24 |
if not title:
|
| 25 |
return "ERROR: No title provided."
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
try:
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
| 31 |
except Exception as e:
|
| 32 |
-
return f"ERROR:
|
| 33 |
|
| 34 |
-
if not
|
|
|
|
|
|
|
| 35 |
return (
|
| 36 |
-
f"ERROR: No
|
| 37 |
-
"
|
|
|
|
| 38 |
)
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
return f"""Title: {movie.get("title")}
|
| 51 |
-
IMDb Rating: {movie.get("rating")}
|
| 52 |
-
Plot: {(movie.get("plot") or [None])[0]}
|
| 53 |
-
Year: {movie.get("year")}
|
| 54 |
-
Genres: {movie.get("genres")}
|
| 55 |
"""
|
|
|
|
| 56 |
|
|
|
|
| 57 |
@dataclass
|
| 58 |
class MovieData:
|
| 59 |
title: str
|
| 60 |
-
|
| 61 |
|
|
|
|
| 62 |
agent = Agent(
|
| 63 |
"openai:gpt-4o-mini",
|
| 64 |
deps_type=MovieData,
|
|
@@ -67,13 +77,13 @@ agent = Agent(
|
|
| 67 |
|
| 68 |
@agent.system_prompt
|
| 69 |
async def movie_system_prompt(ctx: RunContext[MovieData]) -> str:
|
| 70 |
-
return await ctx.deps.
|
| 71 |
|
| 72 |
async def run_agent(user_query: str, movie_title: str):
|
| 73 |
-
deps = MovieData(title=movie_title,
|
| 74 |
|
| 75 |
-
# Preflight
|
| 76 |
-
preflight = await deps.
|
| 77 |
if preflight.startswith("ERROR:"):
|
| 78 |
return {"Error": preflight.replace("ERROR:", "").strip()}
|
| 79 |
|
|
@@ -84,14 +94,14 @@ async def run_agent(user_query: str, movie_title: str):
|
|
| 84 |
"Recommend": bool(result.output.recommended),
|
| 85 |
}
|
| 86 |
|
|
|
|
| 87 |
with gr.Blocks() as demo:
|
| 88 |
-
gr.Markdown("## Movie Recommender Agent")
|
| 89 |
user_query = gr.Textbox(label="What is your preference? (e.g. 'I like supernatural horror movies')")
|
| 90 |
-
movie_title = gr.Textbox(label="Movie name (e.g. 'The Conjuring')")
|
| 91 |
output = gr.JSON(label="Agent Recommendation")
|
| 92 |
-
submit_btn = gr.Button("Check Movie")
|
| 93 |
|
| 94 |
-
|
| 95 |
submit_btn.click(fn=run_agent, inputs=[user_query, movie_title], outputs=output)
|
| 96 |
|
| 97 |
if __name__ == "__main__":
|
|
|
|
|
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
+
from pydantic import BaseModel, Field
|
|
|
|
| 3 |
from pydantic_ai import Agent, RunContext
|
| 4 |
import gradio as gr
|
| 5 |
import os
|
| 6 |
+
import httpx
|
| 7 |
|
| 8 |
+
# ---- Secrets ----
|
| 9 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 10 |
if not OPENAI_API_KEY:
|
| 11 |
raise RuntimeError("Missing OPENAI_API_KEY. Add it in Spaces → Settings → Secrets.")
|
| 12 |
|
| 13 |
+
OMDB_API_KEY = os.getenv("OMDB_API_KEY")
|
| 14 |
+
if not OMDB_API_KEY:
|
| 15 |
+
raise RuntimeError("Missing OMDB_API_KEY. Add it in Spaces → Settings → Secrets.")
|
| 16 |
+
|
| 17 |
+
# ---- Output schema ----
|
| 18 |
class Review(BaseModel):
|
| 19 |
why: str
|
| 20 |
+
rating: int = Field(ge=1, le=10)
|
| 21 |
recommended: bool
|
| 22 |
|
| 23 |
+
# ---- OMDb connector ----
|
| 24 |
+
class OMDbConnection:
|
| 25 |
+
def __init__(self, api_key: str):
|
| 26 |
+
self.api_key = api_key
|
| 27 |
+
self.base_url = "https://www.omdbapi.com/"
|
| 28 |
|
| 29 |
async def get_movie_info(self, title: str) -> str:
|
| 30 |
title = (title or "").strip()
|
| 31 |
if not title:
|
| 32 |
return "ERROR: No title provided."
|
| 33 |
|
| 34 |
+
params = {"apikey": self.api_key, "t": title, "plot": "short"}
|
|
|
|
| 35 |
try:
|
| 36 |
+
async with httpx.AsyncClient(timeout=15) as client:
|
| 37 |
+
r = await client.get(self.base_url, params=params)
|
| 38 |
+
r.raise_for_status()
|
| 39 |
+
data = r.json()
|
| 40 |
except Exception as e:
|
| 41 |
+
return f"ERROR: OMDb request failed: {type(e).__name__}: {e}"
|
| 42 |
|
| 43 |
+
if not data or data.get("Response") != "True":
|
| 44 |
+
# OMDb returns Response=False with an Error message
|
| 45 |
+
msg = data.get("Error") if isinstance(data, dict) else None
|
| 46 |
return (
|
| 47 |
+
f"ERROR: No OMDb results found for: {title}"
|
| 48 |
+
+ (f" ({msg})" if msg else "")
|
| 49 |
+
+ "\nTry adding the year, e.g. 'The Conjuring (2013)'."
|
| 50 |
)
|
| 51 |
|
| 52 |
+
# Build system prompt from OMDb fields
|
| 53 |
+
system_prompt = f"""Title: {data.get("Title")}
|
| 54 |
+
Year: {data.get("Year")}
|
| 55 |
+
Rated: {data.get("Rated")}
|
| 56 |
+
Runtime: {data.get("Runtime")}
|
| 57 |
+
Genre: {data.get("Genre")}
|
| 58 |
+
Director: {data.get("Director")}
|
| 59 |
+
Actors: {data.get("Actors")}
|
| 60 |
+
IMDb Rating: {data.get("imdbRating")}
|
| 61 |
+
Plot: {data.get("Plot")}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
"""
|
| 63 |
+
return system_prompt
|
| 64 |
|
| 65 |
+
# ---- Deps container ----
|
| 66 |
@dataclass
|
| 67 |
class MovieData:
|
| 68 |
title: str
|
| 69 |
+
omdb_conn: OMDbConnection
|
| 70 |
|
| 71 |
+
# ---- Agent ----
|
| 72 |
agent = Agent(
|
| 73 |
"openai:gpt-4o-mini",
|
| 74 |
deps_type=MovieData,
|
|
|
|
| 77 |
|
| 78 |
@agent.system_prompt
|
| 79 |
async def movie_system_prompt(ctx: RunContext[MovieData]) -> str:
|
| 80 |
+
return await ctx.deps.omdb_conn.get_movie_info(ctx.deps.title)
|
| 81 |
|
| 82 |
async def run_agent(user_query: str, movie_title: str):
|
| 83 |
+
deps = MovieData(title=movie_title, omdb_conn=OMDbConnection(OMDB_API_KEY))
|
| 84 |
|
| 85 |
+
# Preflight so we return clean UI errors (and avoid LLM call on missing data)
|
| 86 |
+
preflight = await deps.omdb_conn.get_movie_info(movie_title)
|
| 87 |
if preflight.startswith("ERROR:"):
|
| 88 |
return {"Error": preflight.replace("ERROR:", "").strip()}
|
| 89 |
|
|
|
|
| 94 |
"Recommend": bool(result.output.recommended),
|
| 95 |
}
|
| 96 |
|
| 97 |
+
# ---- Gradio UI ----
|
| 98 |
with gr.Blocks() as demo:
|
| 99 |
+
gr.Markdown("## Movie Recommender Agent (OMDb)")
|
| 100 |
user_query = gr.Textbox(label="What is your preference? (e.g. 'I like supernatural horror movies')")
|
| 101 |
+
movie_title = gr.Textbox(label="Movie name (e.g. 'The Conjuring (2013)')")
|
| 102 |
output = gr.JSON(label="Agent Recommendation")
|
|
|
|
| 103 |
|
| 104 |
+
submit_btn = gr.Button("Check Movie")
|
| 105 |
submit_btn.click(fn=run_agent, inputs=[user_query, movie_title], outputs=output)
|
| 106 |
|
| 107 |
if __name__ == "__main__":
|
requirements.txt
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
imdbpy
|
| 2 |
python-dotenv
|
| 3 |
pydantic
|
| 4 |
pydantic-ai
|
|
|
|
|
|
|
| 1 |
python-dotenv
|
| 2 |
pydantic
|
| 3 |
pydantic-ai
|