Spaces:
Sleeping
Sleeping
File size: 6,593 Bytes
af59dca fd367c0 af59dca fd367c0 af59dca fd367c0 af59dca fd367c0 af59dca fd367c0 af59dca d018685 af59dca fd367c0 af59dca fd367c0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | # app.py
import os
import uuid
import gradio as gr
from langchain_core.chat_history import (
InMemoryChatMessageHistory,
BaseChatMessageHistory,
)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.runnables import RunnableLambda, RunnableParallel
import os
from core import answer_as_table
# Ensure GOOGLE_API_KEY is set in environment before running.
# Example:
# os.environ["GOOGLE_API_KEY"] = "your-google-api-key"
# Prompt scaffolding (used only to satisfy history insertion)
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful academic assistant that generates literature reviews.",
),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"),
]
)
def identity(inputs: dict) -> dict:
return {
"question": (inputs.get("question") or "").strip(),
"use_web": bool(inputs.get("use_web", False)),
"region": (inputs.get("region") or "us-en"),
"safesearch": (inputs.get("safesearch") or "moderate"),
"timelimit": (inputs.get("timelimit") or None),
"backend": (inputs.get("backend") or None),
"max_results": int(inputs.get("max_results") or 20),
}
id_runnable = RunnableLambda(identity)
def _orchestrate(inputs: dict) -> str:
text = (inputs.get("question") or "").strip()
use_web = bool(inputs.get("use_web", False))
region = inputs.get("region") or "us-en"
safesearch = inputs.get("safesearch") or "moderate"
timelimit = inputs.get("timelimit") or None
backend = inputs.get("backend") or None
max_results = int(inputs.get("max_results") or 20)
if not text:
# Return a small info table only when user provided nothing
return (
"| Intent | Reply |\n"
"|--------|-------|\n"
"| Help | Please enter a research topic or a message. |\n"
)
# Route to web TABLE or plain chat text depending on use_web
return answer_as_table(
text,
region=region,
max_results=max_results,
safesearch=safesearch,
timelimit=timelimit,
backend=backend,
force_web=use_web,
)
core_runnable = RunnableLambda(_orchestrate)
# Run prompt and identity in parallel, then pick the identity output to feed core.
# Prompt runs solely to let RunnableWithMessageHistory insert 'history'.
combined = (
RunnableParallel(prompt=prompt, data=id_runnable).pick("data")
) | core_runnable
# Session-scoped history
_store: dict[str, BaseChatMessageHistory] = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in _store:
_store[session_id] = InMemoryChatMessageHistory()
return _store[session_id]
with_history = RunnableWithMessageHistory(
combined,
get_session_history,
input_messages_key="question",
history_messages_key="history",
) # requires config={"configurable": {"session_id": "<id>"}} on invoke
def respond(
message,
history,
use_web,
session_state,
region,
safesearch,
timelimit,
backend,
max_results,
):
"""
- message: dict or str (ChatInterface type='messages' passes a dict with 'text')
- history: UI history (Gradio-managed; LangChain history is separate)
- use_web: checkbox
- session_state: gr.State carrying a stable session_id to isolate histories across users
- region, safesearch, timelimit, backend, max_results: web search controls
"""
text = (message.get("text") if isinstance(message, dict) else message) or ""
text = text.strip()
if not text:
return (
"| Intent | Reply |\n"
"|--------|-------|\n"
"| Help | Please enter a research topic or a message. |\n"
), session_state
# Ensure a per-user session id for RunnableWithMessageHistory
session_id = session_state.get("session_id")
if not session_id:
session_id = f"conv-{uuid.uuid4().hex}"
session_state["session_id"] = session_id
try:
output = with_history.invoke(
{
"question": text,
"use_web": bool(use_web),
"region": (region or "us-en"),
"safesearch": (safesearch or "moderate"),
"timelimit": (timelimit or None),
"backend": (backend or None),
"max_results": int(max_results or 20),
},
config={"configurable": {"session_id": session_id}},
)
# output is either a Markdown TABLE (web) or plain chat text (no web)
return output, session_state
except Exception as e:
return (
f"| Intent | Reply |\n|--------|-------|\n| Error | {str(e)} |\n"
), session_state
with gr.Blocks(title="Literature Review Chat") as demo:
gr.Markdown(
"Enter a research topic to generate a Markdown literature review table (enable web), or chat for quick help (plain text)."
)
session_state = gr.State(
{"session_id": None}
) # session-persistent state in the browser tab
with gr.Row():
use_web = gr.Checkbox(label="Use web search (academic sources)", value=True)
region = gr.Dropdown(
choices=["us-en", "wt-wt", "uk-en", "ca-en", "in-en", "de-de", "fr-fr"],
value="us-en",
label="Region",
)
safesearch = gr.Dropdown(
choices=["on", "moderate", "off"], value="moderate", label="SafeSearch"
)
timelimit = gr.Dropdown(
choices=[None, "d", "w", "m", "y"], value=None, label="Time limit"
)
backend = gr.Dropdown(
choices=[None, "api", "html", "lite"], value=None, label="DDG backend"
)
max_results = gr.Slider(
minimum=5, maximum=50, value=20, step=1, label="Max results"
)
chat = gr.ChatInterface(
fn=respond,
additional_inputs=[
use_web,
session_state,
region,
safesearch,
timelimit,
backend,
max_results,
],
additional_outputs=[session_state],
type="messages",
title="Literature Review Chat",
description="Toggle the checkbox to search the web and produce a literature review table; otherwise, get a concise plain-text chat reply.",
save_history=True,
)
if __name__ == "__main__":
demo.launch()
|