Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions dref/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@

ENCODING_NAME = "cl100k_base"

MAX_OUTPUT_CHARS_PER_FIELD = 1500
MAX_INPUT_TOKENS = 10000


# The models a DrefSummary can be generated from.
DrefSummarySource = Union[Dref, DrefOperationalUpdate, DrefFinalReport]

Expand All @@ -44,7 +42,8 @@
"You are an IFRC expert analyst specializing in DREF (Disaster Response Emergency Fund) "
"operations. Analyze the provided DREF data and produce clear, professional humanitarian "
"summaries suitable for IFRC staff and National Society personnel. Use only the information "
"provided in the data; do not invent facts, figures, or details."
"provided in the data; do not invent facts, figures, or details. Where supporting information "
"for a section is absent, return an empty string for that section rather than speculating."
)

# Section prompt builders
Expand Down Expand Up @@ -90,7 +89,7 @@ def _build_lessons_learned_prompt(**kwargs) -> str:
}

GLOBAL_PROMPT = (
"The DREF data above is organised by summary section. Using ONLY that data, write five concise "
"The DREF data above is organised by summary section. Using ONLY that data, write five "
"summary sections. Return a single JSON object (and nothing else) with exactly these keys, each "
"summarising the block of the same name:\n"
"\n"
Expand All @@ -109,8 +108,14 @@ def _build_lessons_learned_prompt(**kwargs) -> str:
"- Summarise only what each section's data actually contains; do not add topics or details it "
"does not mention.\n"
"- Preserve every specific figure, location and timeframe from the source; never fabricate them.\n"
"- Each value must be plain text (no markdown, no bullet lists, no nested JSON): one "
"well-structured paragraph in professional humanitarian language.\n"
"- If a section's data block is empty or holds no usable content, set that key to an empty "
"string. Never write a sentence stating that data is missing, not provided or not recorded.\n"
"- Scale each section's length to how much its source data actually contains. Never "
"pad a thin section to reach a length, and never compress a detailed one into a single "
"paragraph — a long source deserves a correspondingly long summary.\n"
"- Each value must be plain text (no markdown, no bullet lists, no nested JSON): one or more "
"well-structured paragraphs in professional humanitarian language, separated by a blank line, "
"or an empty string when that section has no data.\n"
"- Return ONLY the JSON object, with no surrounding prose or code fences."
)

Expand Down Expand Up @@ -138,6 +143,9 @@ def _extract_fields(obj, field_names: List[str]) -> dict:

SITUATIONAL_COMMON_FIELDS: List[str] = ["event_description", "event_scope"]

# Imminent DREF applications created on the v2 use hazard_date_and_location.
IMMINENT_SITUATIONAL_FIELDS: List[str] = ["hazard_date_and_location"]

OPERATIONAL_COMMON_FIELDS: List[str] = ["operation_objective", "response_strategy"]

PEOPLE_COMMON_FIELDS: List[str] = ["people_assisted", "selection_criteria"]
Expand All @@ -151,14 +159,10 @@ def __init__(self):

@staticmethod
def _situational_overview_kwargs(source_doc) -> dict:
"""Build situational_overview kwargs — common across all document types.

``event_scope`` is one of the common fields; when it is empty (e.g. an
Imminent DREF Application where the scope is not yet known)
``_extract_fields`` drops it automatically, while by the Final Report
stage the event has materialized and the field feeds the summary.
"""
return _extract_fields(source_doc, SITUATIONAL_COMMON_FIELDS)
"""Imminent v2 applications describe the situation in the scenario analysis fields; others use the common ones."""
if isinstance(source_doc, Dref) and source_doc.type_of_dref == Dref.DrefType.IMMINENT and source_doc.is_dref_imminent_v2:
return _extract_fields(source_doc, IMMINENT_SITUATIONAL_FIELDS)
return _extract_fields(source_doc, SITUATIONAL_COMMON_FIELDS) # event_scope is empty for Assessment; dropped

@staticmethod
def _challenges_and_lessons_kwargs(source_doc) -> Dict[str, dict]:
Expand Down Expand Up @@ -345,8 +349,5 @@ def generate_all(self, source_doc: DrefSummarySource, section_kwargs: Optional[D
value = parsed.get(field_name)
if not isinstance(value, str):
continue
summary = value.strip()
if len(summary) > MAX_OUTPUT_CHARS_PER_FIELD:
summary = summary[:MAX_OUTPUT_CHARS_PER_FIELD].rstrip()
results[field_name] = summary
results[field_name] = value.strip()
return results
2 changes: 1 addition & 1 deletion main/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def client(self):
return AzureOpenAI(
azure_endpoint=settings.AZURE_OPENAI_ENDPOINT,
api_key=settings.AZURE_OPENAI_API_KEY,
api_version="2023-05-15",
api_version=settings.AZURE_OPENAI_API_VERSION,
)

def get_response(self, messages: Messages) -> Optional[str]:
Expand Down
4 changes: 4 additions & 0 deletions main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@
AZURE_OPENAI_ENDPOINT=(str, None),
AZURE_OPENAI_API_KEY=(str, None),
AZURE_OPENAI_DEPLOYMENT_NAME=(str, None),
# Azure OpenAI REST api-version. Keep this on a GA (non-preview) release; a
# deployment can be pinned back to an older one without a code change.
AZURE_OPENAI_API_VERSION=(str, "2024-10-21"),
# Use a fake LLM client instead of calling Azure OpenAI
USE_DUMMY_LLM_CLIENT=(bool, False),
# ReliefWeb appname
Expand Down Expand Up @@ -870,6 +873,7 @@ def decode_base64(env_key, fallback_env_key):
AZURE_OPENAI_ENDPOINT = env("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_API_KEY = env("AZURE_OPENAI_API_KEY")
AZURE_OPENAI_DEPLOYMENT_NAME = env("AZURE_OPENAI_DEPLOYMENT_NAME")
AZURE_OPENAI_API_VERSION = env("AZURE_OPENAI_API_VERSION")
USE_DUMMY_LLM_CLIENT = env("USE_DUMMY_LLM_CLIENT")

OIDC_ENABLE = env("OIDC_ENABLE")
Expand Down
Loading