Spaces:
Runtime error
Runtime error
File size: 7,511 Bytes
5b91be2 2381bf9 703c1e7 c564692 7c8aa49 d0c93af 8c33138 5d21b80 d0c93af 703c1e7 d0c93af 2381bf9 d0c93af 5d21b80 2381bf9 8c33138 d0c93af 2381bf9 5d21b80 d0c93af 703c1e7 5d21b80 8c33138 703c1e7 5d21b80 8c33138 5d21b80 8c33138 5d21b80 8c33138 703c1e7 8c33138 5d21b80 d0c93af 5d21b80 d0c93af 5b91be2 d0c93af 5d21b80 8c33138 d0c93af 5d21b80 d0c93af 5d21b80 d0c93af 703c1e7 c564692 5d21b80 703c1e7 2381bf9 8c33138 703c1e7 8c33138 c564692 5d21b80 703c1e7 d0c93af 703c1e7 d0c93af 5d21b80 d0c93af 8c33138 2381bf9 8c33138 88ba2a8 8c33138 5d21b80 d0c93af 8c33138 9f9e436 8c33138 d0c93af 2381bf9 d0c93af 2381bf9 9f9e436 2381bf9 8c33138 d0c93af 2381bf9 5b91be2 5d21b80 8c33138 c564692 5d21b80 8c33138 5d21b80 d0c93af 5d21b80 d0c93af 5d21b80 d0c93af 703c1e7 d0c93af | 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 | import streamlit as st
import warnings
import os
from pathlib import Path
# Suppress warnings yang tidak perlu
warnings.filterwarnings("ignore", category=UserWarning)
warnings.filterwarnings("ignore", message=".*torchvision.*")
warnings.filterwarnings("ignore", message=".*UNEXPECTED.*")
from google import genai
from google.genai import types # β Penting
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.documents import Document
# ================== CONFIG ==================
st.set_page_config(
page_title="Human+ Lab-to-Protocol AI",
page_icon="π§¬",
layout="centered"
)
st.title("𧬠Human+ Lab-to-Protocol AI")
st.markdown("""
**Upload hasil lab Anda β Dapatkan rekomendasi IV drip & protocol personal**
*Semua data diproses sementara dan hilang setelah sesi selesai (privacy-first)*
""")
# ================== GEMINI CLIENT ==================
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
if not GEMINI_API_KEY:
st.error("β GEMINI_API_KEY tidak ditemukan. Tambahkan di Settings β Secrets.")
st.stop()
try:
client = genai.Client(api_key=GEMINI_API_KEY)
MODEL_NAME = "gemini-2.5-flash"
st.success("β
Gemini client siap")
except Exception as e:
st.error(f"Gagal menginisialisasi Gemini: {str(e)}")
st.stop()
# ================== LOAD KNOWLEDGE BASE ==================
@st.cache_resource(show_spinner=False, ttl=7200)
def load_knowledge_base():
with st.spinner("β³ Memuat knowledge base Human+..."):
try:
knowledge_dir = Path("/app/knowledge")
if not knowledge_dir.exists():
st.error(f"β Folder knowledge tidak ditemukan di: {knowledge_dir}")
return None
documents = []
for file_path in knowledge_dir.glob("*.md"):
with open(file_path, "r", encoding="utf-8") as f:
content = f.read().strip()
if content:
documents.append(
Document(
page_content=content,
metadata={"source": file_path.name}
)
)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=150,
separators=["\n\n## ", "\n\n### ", "\n\n", "\n", " ", "."]
)
chunks = text_splitter.split_documents(documents)
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-mpnet-base-v2"
)
vector_store = FAISS.from_documents(chunks, embeddings)
st.success(f"β
Knowledge base siap ({len(documents)} file β’ {len(chunks)} chunks)")
return vector_store
except Exception as e:
st.error(f"Gagal memuat knowledge base: {str(e)}")
return None
vector_store = load_knowledge_base()
if vector_store is None:
st.stop()
# ================== UPLOAD & ANALYSIS ==================
uploaded_file = st.file_uploader(
"Upload hasil lab (PDF)",
type=["pdf"],
help="Maksimal 10MB. Semua data hanya diproses sementara."
)
if uploaded_file:
if uploaded_file.size > 10 * 1024 * 1024:
st.error("β File terlalu besar. Maksimal 10MB.")
st.stop()
pdf_bytes = uploaded_file.read()
with st.spinner("Menganalisis hasil lab menggunakan Gemini..."):
try:
pdf_part = types.Part.from_bytes(
data=pdf_bytes,
mime_type="application/pdf"
)
# Perbaikan utama: gunakan config=GenerateContentConfig
response = client.models.generate_content(
model=MODEL_NAME,
contents=[
"Extract semua biomarker penting dari hasil lab PDF ini dalam format JSON yang terstruktur. "
"Sertakan nama tes, nilai, unit, reference range, dan flag (high/low/normal). "
"Fokus pada marker longevity: hs-CRP, Homocysteine, Vitamin D, HbA1c, Testosterone, "
"Magnesium, Zinc, B12, Ferritin, ApoB, dll.",
pdf_part
],
config=types.GenerateContentConfig(
temperature=0.1,
max_output_tokens=4096
)
)
extracted_text = response.text
st.success("β
Lab berhasil dianalisis!")
with st.expander("π Hasil Ekstraksi Biomarker"):
st.markdown(extracted_text)
except Exception as e:
st.error(f"Gagal menganalisis PDF: {str(e)}")
st.stop()
# ================== GENERATE PROTOCOL ==================
if st.button("π¬ Generate Human+ Personalized Protocol", type="primary"):
with st.spinner("Membuat rekomendasi protocol hyperpersonalized..."):
try:
retriever = vector_store.as_retriever(
search_type="mmr",
search_kwargs={"k": 8, "fetch_k": 20, "lambda_mult": 0.7}
)
relevant_docs = retriever.invoke(extracted_text)
context = "\n\n".join([
f"[Sumber: {doc.metadata.get('source', 'unknown')}] {doc.page_content}"
for doc in relevant_docs
])
prompt = f"""
Kamu adalah AI Longevity Specialist resmi dari **Human+ Bali**, didirikan oleh Benjamin White (@bennywhitethatsright).
Filosofi utama: **"From Injured β Optimized"**
Hasil lab pasien:
{extracted_text}
Knowledge base Human+:
{context}
Buatlah rekomendasi protocol yang jelas, positif, empowering, dan actionable dengan struktur berikut:
1. **Ringkasan Temuan Utama** (apa yang paling perlu dioptimalkan + motivasi)
2. **Rekomendasi IV Drip** (The All-In atau varian custom + komposisi)
3. **Suplemen Harian** (dosis & timing)
4. **Lifestyle & Recovery Protocol** (ice bath, structured water, breathing, sunlight)
5. **Jadwal Retest & Next Step**
Gunakan bahasa Indonesia yang mudah dipahami dan sesuai voice Benjamin White. Akhiri dengan semangat "From Injured β Optimized".
"""
response = client.models.generate_content(
model=MODEL_NAME,
contents=[prompt],
config=types.GenerateContentConfig(
temperature=0.4,
max_output_tokens=4096
)
)
protocol = response.text
st.subheader("π Rekomendasi Protocol Personal dari Human+")
st.markdown(protocol)
report_text = f"Human+ Lab-to-Protocol Report\n\n{protocol}"
st.download_button(
label="π₯ Download Report",
data=report_text,
file_name="Human+_Protocol_Report.txt",
mime="text/plain"
)
except Exception as e:
st.error(f"Gagal generate protocol: {str(e)}")
# Footer
st.divider()
st.caption("""
**Privacy Note**: Aplikasi ini 100% stateless. Tidak ada data yang disimpan di server.
Dibuat untuk Human+ Bali β’ From Injured β Optimized
""") |