Spaces:
Sleeping
Sleeping
Commit
·
7a1d7bb
1
Parent(s):
a6a49bb
Add application file
Browse files- agent.py +89 -0
- app.py +95 -0
- medicare_and_you.md +0 -0
- pdf_to_markdown.py +30 -0
- requirements.txt +5 -0
agent.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
from google.adk.agents import Agent
|
| 5 |
+
from google.genai import types as genai_types
|
| 6 |
+
from google.adk.runners import Runner
|
| 7 |
+
from google.adk.sessions.in_memory_session_service import InMemorySessionService
|
| 8 |
+
|
| 9 |
+
# Standard Python libraries
|
| 10 |
+
import warnings
|
| 11 |
+
import logging
|
| 12 |
+
import os
|
| 13 |
+
import dotenv
|
| 14 |
+
|
| 15 |
+
dotenv.load_dotenv()
|
| 16 |
+
|
| 17 |
+
# --- Basic Configuration ---
|
| 18 |
+
warnings.filterwarnings("ignore") # Suppress common warnings
|
| 19 |
+
logging.basicConfig(level=logging.ERROR) # Reduce log verbosity
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
logger.info("Initializing new session")
|
| 24 |
+
# Create session service and runner
|
| 25 |
+
|
| 26 |
+
def read_markdown_file() -> str:
|
| 27 |
+
"""Read the Medicare and You markdown file."""
|
| 28 |
+
markdown_path = Path("medicare_and_you.md")
|
| 29 |
+
if not markdown_path.exists():
|
| 30 |
+
raise FileNotFoundError("medicare_and_you.md not found")
|
| 31 |
+
return markdown_path.read_text()
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# Main Medicare agent
|
| 35 |
+
root_agent = Agent(
|
| 36 |
+
name="medicare_assistant",
|
| 37 |
+
model="gemini-2.0-flash-lite",
|
| 38 |
+
description="Helps users understand Medicare benefits using the Medicare & You handbook",
|
| 39 |
+
instruction=f"""
|
| 40 |
+
You are a caring Medicare guide. Your job is to help people understand their Medicare benefits
|
| 41 |
+
using information from the Medicare & You 2025 handbook. Fall back to google search if you don't have the information.
|
| 42 |
+
|
| 43 |
+
Here is the complete Medicare & You 2025 handbook content that you should use to answer questions:
|
| 44 |
+
|
| 45 |
+
{read_markdown_file()}
|
| 46 |
+
|
| 47 |
+
When answering questions, always:
|
| 48 |
+
|
| 49 |
+
1. Use only the handbook content provided above
|
| 50 |
+
2. Explain concepts in simple, friendly terms
|
| 51 |
+
3. Break down complex topics into easy steps
|
| 52 |
+
4. If unsure, ask clarifying questions
|
| 53 |
+
5. End with a note saying to refer tothe page number with the markdown link to the Medicare & You handbook, where the markdown would look like [Medicare & You Handbook](https://www.medicare.gov/publications/10050-medicare-and-you.pdf)
|
| 54 |
+
6. No need to ask if the user has additional questions, just answer the question.
|
| 55 |
+
|
| 56 |
+
Remember: Be warm, patient, and focus on making Medicare easy to understand.
|
| 57 |
+
""",
|
| 58 |
+
tools=[],
|
| 59 |
+
generate_content_config=genai_types.GenerateContentConfig(
|
| 60 |
+
temperature=0.1
|
| 61 |
+
)
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
# @title Define Agent Interaction Function
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
async def call_agent_async(query: str, runner, user_id, session_id):
|
| 68 |
+
"""Sends a query to the agent and prints the final response."""
|
| 69 |
+
|
| 70 |
+
# Prepare the user's message in ADK format
|
| 71 |
+
content = genai_types.Content(role='user', parts=[genai_types.Part(text=query)])
|
| 72 |
+
|
| 73 |
+
final_response_text = "Agent did not produce a final response." # Default
|
| 74 |
+
|
| 75 |
+
# Key Concept: run_async executes the agent logic and yields Events.
|
| 76 |
+
# We iterate through events to find the final answer.
|
| 77 |
+
async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content):
|
| 78 |
+
if event.is_final_response():
|
| 79 |
+
if event.content and event.content.parts:
|
| 80 |
+
# Assuming text response in the first part
|
| 81 |
+
final_response_text = event.content.parts[0].text
|
| 82 |
+
elif event.actions and event.actions.escalate: # Handle potential errors/escalations
|
| 83 |
+
final_response_text = f"Agent escalated: {event.error_message or 'No specific message.'}"
|
| 84 |
+
# Add more checks here if needed (e.g., specific error codes)
|
| 85 |
+
break # Stop processing events once the final response is found
|
| 86 |
+
|
| 87 |
+
return final_response_text
|
| 88 |
+
|
| 89 |
+
|
app.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import asyncio
|
| 3 |
+
|
| 4 |
+
from agent import root_agent, call_agent_async
|
| 5 |
+
from google.adk.runners import Runner
|
| 6 |
+
from google.adk.sessions.in_memory_session_service import InMemorySessionService
|
| 7 |
+
|
| 8 |
+
# Standard Python libraries
|
| 9 |
+
import warnings
|
| 10 |
+
import logging
|
| 11 |
+
|
| 12 |
+
import dotenv
|
| 13 |
+
import uuid
|
| 14 |
+
# --- Asyncio Configuration for Streamlit ---
|
| 15 |
+
# Required to run async ADK functions within Streamlit's synchronous environment
|
| 16 |
+
import nest_asyncio
|
| 17 |
+
nest_asyncio.apply()
|
| 18 |
+
|
| 19 |
+
dotenv.load_dotenv()
|
| 20 |
+
|
| 21 |
+
# --- Basic Configuration ---
|
| 22 |
+
warnings.filterwarnings("ignore") # Suppress common warnings
|
| 23 |
+
logging.basicConfig(level=logging.WARNING) # Reduce log verbosity
|
| 24 |
+
logger = logging.getLogger(__name__)
|
| 25 |
+
|
| 26 |
+
st.title("🏥 Medicare Guide!")
|
| 27 |
+
|
| 28 |
+
# Show welcome message only on first load
|
| 29 |
+
if "welcome_shown" not in st.session_state:
|
| 30 |
+
st.session_state.welcome_shown = True
|
| 31 |
+
st.session_state.user_id = f"streamlit_user_{uuid.uuid4()}"
|
| 32 |
+
st.session_state.session_id = f"streamlit_session_{uuid.uuid4()}"
|
| 33 |
+
# Main welcome message
|
| 34 |
+
st.markdown("""
|
| 35 |
+
### 💡 Examples of questions you can ask me:
|
| 36 |
+
- "What are the different parts of Medicare?"
|
| 37 |
+
- "When can I enroll in Medicare?"
|
| 38 |
+
- "What's the difference between Original Medicare and Medicare Advantage?"
|
| 39 |
+
""")
|
| 40 |
+
|
| 41 |
+
# Disclaimer
|
| 42 |
+
st.info("🔔 Note: I provide information from the official [Medicare & You Handbook 2025](https://www.medicare.gov/publications/10050-medicare-and-you.pdf), but am not perfect! I will always do my best to provide page numbers, and I strongly suggest checking for yourself.")
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
if "adk_session" not in st.session_state:
|
| 46 |
+
app_name="Medicare Guide"
|
| 47 |
+
session_service = InMemorySessionService()
|
| 48 |
+
adk_session = session_service.create_session(
|
| 49 |
+
app_name=app_name,
|
| 50 |
+
user_id=st.session_state.user_id,
|
| 51 |
+
session_id=st.session_state.session_id,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
runner = Runner(
|
| 55 |
+
app_name="Medicare Guide",
|
| 56 |
+
agent=root_agent,
|
| 57 |
+
session_service=session_service,
|
| 58 |
+
)
|
| 59 |
+
st.session_state.adk_session = adk_session
|
| 60 |
+
st.session_state.runner = runner
|
| 61 |
+
st.session_state.session_service = session_service
|
| 62 |
+
st.session_state.app_name = app_name
|
| 63 |
+
|
| 64 |
+
runner = st.session_state.runner
|
| 65 |
+
session_service = st.session_state.session_service
|
| 66 |
+
app_name = st.session_state.app_name
|
| 67 |
+
|
| 68 |
+
# Initialize chat history
|
| 69 |
+
if "messages" not in st.session_state:
|
| 70 |
+
st.session_state.messages = []
|
| 71 |
+
|
| 72 |
+
# Display chat messages from history on app rerun
|
| 73 |
+
for message in st.session_state.messages:
|
| 74 |
+
with st.chat_message(message["role"]):
|
| 75 |
+
st.markdown(message["content"], unsafe_allow_html=True)
|
| 76 |
+
|
| 77 |
+
# Accept user input
|
| 78 |
+
if prompt := st.chat_input("What can I answer for you about Medicare?"):
|
| 79 |
+
# Add user message to chat history
|
| 80 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 81 |
+
# Display user message in chat message container
|
| 82 |
+
with st.chat_message("user"):
|
| 83 |
+
st.markdown(prompt, unsafe_allow_html=True)
|
| 84 |
+
|
| 85 |
+
# Display assistant response in chat message container
|
| 86 |
+
with st.chat_message("assistant"):
|
| 87 |
+
try:
|
| 88 |
+
response_text = asyncio.run(call_agent_async(prompt, runner, st.session_state.user_id, st.session_state.session_id))
|
| 89 |
+
st.markdown(response_text, unsafe_allow_html=True)
|
| 90 |
+
except Exception as e:
|
| 91 |
+
st.error(f"An error occurred during agent interaction: {e}")
|
| 92 |
+
response_text = f"Sorry, a critical error occurred: {e}"
|
| 93 |
+
logger.error(f"An error occurred during agent interaction: {e}", exc_info=True)
|
| 94 |
+
|
| 95 |
+
st.session_state.messages.append({"role": "assistant", "content": response_text})
|
medicare_and_you.md
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pdf_to_markdown.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pymupdf4llm
|
| 2 |
+
import pathlib
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
|
| 5 |
+
def convert_pdf_to_markdown(input_pdf, output_md):
|
| 6 |
+
# Get the markdown text
|
| 7 |
+
print(f"Converting {input_pdf} to markdown...")
|
| 8 |
+
md_text = pymupdf4llm.to_markdown(input_pdf)
|
| 9 |
+
|
| 10 |
+
# Add a header with metadata
|
| 11 |
+
header = f"""# Medicare and You Documentation
|
| 12 |
+
> Converted from PDF on {datetime.now().strftime('%Y-%m-%d')}
|
| 13 |
+
> Source: {input_pdf}
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
# Combine header and content
|
| 20 |
+
final_content = header + md_text
|
| 21 |
+
|
| 22 |
+
# Write to file
|
| 23 |
+
print(f"Writing markdown to {output_md}...")
|
| 24 |
+
pathlib.Path(output_md).write_bytes(final_content.encode())
|
| 25 |
+
print("Conversion complete!")
|
| 26 |
+
|
| 27 |
+
if __name__ == "__main__":
|
| 28 |
+
input_pdf = "10050-medicare-and-you.pdf"
|
| 29 |
+
output_md = "medicare_and_you.md"
|
| 30 |
+
convert_pdf_to_markdown(input_pdf, output_md)
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
huggingface_hub
|
| 2 |
+
streamlit
|
| 3 |
+
google-adk
|
| 4 |
+
openai
|
| 5 |
+
pymupdf4llm
|