Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
File size: 5,163 Bytes
aebc985 5e4d104 aebc985 5e4d104 aebc985 5e4d104 aebc985 d8bc0a6 aebc985 ddeda43 aebc985 ddeda43 aebc985 4bb94c0 1619e8d 4bb94c0 aebc985 |
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 |
import re
import requests
import streamlit as st
def truncate_to_tokens(text, max_tokens):
"""
Truncate a text to an approximate token count by splitting on whitespace.
Args:
text (str): The text to truncate.
max_tokens (int): Maximum number of tokens/words to keep.
Returns:
str: The truncated text.
"""
tokens = text.split()
if len(tokens) > max_tokens:
return " ".join(tokens[:max_tokens])
return text
def build_context_for_result(res, compute_title_fn):
"""
Build a context string (title + objective + description) from a search result.
Args:
res (dict): A result dictionary with 'payload' key containing metadata.
compute_title_fn (callable): Function to compute the title from metadata.
Returns:
str: Combined text from title, objective, and description.
"""
metadata = res.payload.get('metadata', {})
title = metadata.get("title", compute_title_fn(metadata))
objective = metadata.get("objective", "")
desc_en = metadata.get("description.en", "").strip()
desc_de = metadata.get("description.de", "").strip()
description = desc_en if desc_en else desc_de
return f"{title}\n{objective}\n{description}"
def highlight_query(text, query):
"""
Highlight the query text in the given string with simple bold markdown.
Args:
text (str): The full text in which to highlight matches.
query (str): The substring (query) to highlight.
Returns:
str: The markdown-formatted string with highlighted matches.
"""
pattern = re.compile(re.escape(query), re.IGNORECASE)
return pattern.sub(lambda m: f"**{m.group(0)}**", text)
def format_project_id(pid):
"""
Format a numeric project ID into the typical GIZ format (e.g. '201940485' -> '2019.4048.5').
Args:
pid (str|int): The project ID to format.
Returns:
str: Formatted project ID if it has enough digits, otherwise the original string.
"""
s = str(pid)
if len(s) > 5:
return s[:4] + "." + s[4:-1] + "." + s[-1]
return s
def compute_title(metadata):
"""
Compute a default title from metadata using name.en (or name.de if empty).
If an ID is present, append it in brackets.
Args:
metadata (dict): Project metadata dictionary.
Returns:
str: Computed title string or 'No Title'.
"""
name_en = metadata.get("name.en", "").strip()
name_de = metadata.get("name.de", "").strip()
base = name_en if name_en else name_de
pid = metadata.get("id", "")
if base and pid:
return f"{base} [{format_project_id(pid)}]"
return base or "No Title"
def get_rag_answer(query, top_results, endpoint, token):
"""
Send a prompt to the LLM endpoint, including the context from top results.
Args:
query (str): The user question.
top_results (list): List of top search results from which to build context.
endpoint (str): The HuggingFace Inference endpoint URL.
token (str): The Bearer token (from st.secrets, for instance).
Returns:
str: The LLM-generated answer, or an error message if the call fails.
"""
# Build the context
from appStore.rag_utils import truncate_to_tokens, build_context_for_result, compute_title
context = "\n\n".join([build_context_for_result(res, compute_title) for res in top_results])
context = truncate_to_tokens(context,11500) # Truncate to ~11.5k tokens
prompt = (
"You are a project portfolio adviser at the development cooperation GIZ. "
"Using the context below, answer the question in the same language as the question. "
"Your answer must be formatted in bullet points. "
"Ensure that every project title and project number in your answer is wrapped in double asterisks (e.g., **Project Title [2018.2101.6]**) to display them as markdown bold. "
"Include at least one short sentence per project summarizing what the project does in relation to the query. "
"Do not repeat any part of the provided context or the question in your final answer.\n\n"
f"Context:\n{context}\n\n"
f"Question: {query}\n\n"
"Answer:"
)
headers = {"Authorization": f"Bearer {token}"}
payload = {"inputs": prompt, "parameters": {"max_new_tokens": 300}}
response = requests.post(endpoint, headers=headers, json=payload)
if response.status_code == 200:
result = response.json()
answer = result[0].get("generated_text", "")
if "Answer:" in answer:
answer = answer.split("Answer:")[-1].strip()
return answer
elif response.status_code == 503:
# Custom message with a larger llama icon and red highlighted text
return (
"<span style='color: red;'>"
"<span style='font-size: 3em;'>🦙</span> Tzzz Tzzz I'm currently sleeping. "
"Please come back in 10 minutes, and I'll be fully awake to answer your question."
"</span>"
)
else:
return f"Error in generating answer: {response.text}"
|