KayO Codex commited on
Commit
3e5fe33
·
1 Parent(s): 34f7072

Use full-document Q&A and refine ask row

Browse files

Co-authored-by: Codex <codex@openai.com>

Files changed (2) hide show
  1. app.py +15 -12
  2. tests/test_app.py +40 -1
app.py CHANGED
@@ -16,7 +16,6 @@ from services import ingest
16
  from services.providers import get_default_api_key, instantiate_client, list_providers, validate_api_key
17
  from services.rag_pipeline import (
18
  AnalysisResult,
19
- answer_query,
20
  answer_query_from_full_document,
21
  build_cached_document_artifacts,
22
  generate_analysis_progress,
@@ -133,6 +132,12 @@ APP_CSS = """
133
  #analysis-output {
134
  min-height: 100px;
135
  }
 
 
 
 
 
 
136
  #status-output,
137
  #chat-status {
138
  min-height: 1.5rem;
@@ -407,7 +412,7 @@ def _format_chat_entry(answer_text: str, citations: list[dict[str, Any]], *, pro
407
  if provenance == "analysis_based":
408
  prefix = "_Based on the summary and analysis._\n\n"
409
  elif provenance == "full_document":
410
- prefix = "_Deeper full-document answer._\n\n"
411
 
412
  if not citations:
413
  return prefix + answer_text
@@ -701,17 +706,16 @@ def ask_question(
701
  return
702
 
703
  question_text = question.strip()
704
- yield chat_history, session_state, "Searching analysis for an answer...", _deeper_answer_updates(False), ""
705
 
706
  try:
707
  provider_client = instantiate_client(
708
  record["api_config"]["provider"],
709
  record["api_config"]["api_key"],
710
  )
711
- yield chat_history, session_state, "Searching analysis for an answer...", _deeper_answer_updates(False), ""
712
- answer = answer_query(
713
  provider_client,
714
- AnalysisResult.model_validate(record["analysis"]) if record.get("analysis") else None,
715
  record.get("vector_store"),
716
  question_text,
717
  doc_text=record.get("doc_text"),
@@ -731,9 +735,8 @@ def ask_question(
731
  {"role": "assistant", "content": _format_chat_entry(answer.answer, citations, provenance=answer.provenance)}
732
  ]
733
  record["chat_history"] = chat_history
734
- record["pending_deeper_question"] = question_text if answer.deeper_answer_available else None
735
- deeper_hint = answer.consent_prompt if answer.deeper_answer_available else ""
736
- yield chat_history, session_state, "", _deeper_answer_updates(answer.deeper_answer_available), deeper_hint
737
 
738
 
739
  def run_deeper_answer(
@@ -891,15 +894,15 @@ def build_app() -> gr.Blocks:
891
  with gr.Group(elem_id="chat-panel"):
892
  gr.Markdown("## Ask Further Questions")
893
  chatbot = gr.Chatbot(show_label=False)
894
- with gr.Row():
 
895
  with gr.Column(scale=6, min_width=0):
896
- gr.Markdown("Question")
897
  question_input = gr.Textbox(
898
  label="Question",
899
  placeholder="What would you like to know?",
900
  show_label=False,
901
  )
902
- ask_button = gr.Button("Ask", scale=1, variant="primary")
903
  chat_status = gr.Markdown(elem_id="chat-status")
904
  deeper_answer_button = gr.Button("Run deeper full-document answer", visible=False)
905
  deeper_answer_hint = gr.Markdown("")
 
16
  from services.providers import get_default_api_key, instantiate_client, list_providers, validate_api_key
17
  from services.rag_pipeline import (
18
  AnalysisResult,
 
19
  answer_query_from_full_document,
20
  build_cached_document_artifacts,
21
  generate_analysis_progress,
 
132
  #analysis-output {
133
  min-height: 100px;
134
  }
135
+ #chat-question-row {
136
+ align-items: flex-start;
137
+ }
138
+ #ask-button button {
139
+ border-radius: 0.6rem !important;
140
+ }
141
  #status-output,
142
  #chat-status {
143
  min-height: 1.5rem;
 
412
  if provenance == "analysis_based":
413
  prefix = "_Based on the summary and analysis._\n\n"
414
  elif provenance == "full_document":
415
+ prefix = "_Full-document answer._\n\n"
416
 
417
  if not citations:
418
  return prefix + answer_text
 
706
  return
707
 
708
  question_text = question.strip()
709
+ yield chat_history, session_state, "Reading the full document for an answer...", _deeper_answer_updates(False), ""
710
 
711
  try:
712
  provider_client = instantiate_client(
713
  record["api_config"]["provider"],
714
  record["api_config"]["api_key"],
715
  )
716
+ yield chat_history, session_state, "Reading the full document for an answer...", _deeper_answer_updates(False), ""
717
+ answer = answer_query_from_full_document(
718
  provider_client,
 
719
  record.get("vector_store"),
720
  question_text,
721
  doc_text=record.get("doc_text"),
 
735
  {"role": "assistant", "content": _format_chat_entry(answer.answer, citations, provenance=answer.provenance)}
736
  ]
737
  record["chat_history"] = chat_history
738
+ record["pending_deeper_question"] = None
739
+ yield chat_history, session_state, "", _deeper_answer_updates(False), ""
 
740
 
741
 
742
  def run_deeper_answer(
 
894
  with gr.Group(elem_id="chat-panel"):
895
  gr.Markdown("## Ask Further Questions")
896
  chatbot = gr.Chatbot(show_label=False)
897
+ gr.Markdown("Question")
898
+ with gr.Row(elem_id="chat-question-row"):
899
  with gr.Column(scale=6, min_width=0):
 
900
  question_input = gr.Textbox(
901
  label="Question",
902
  placeholder="What would you like to know?",
903
  show_label=False,
904
  )
905
+ ask_button = gr.Button("Ask", scale=1, variant="primary", elem_id="ask-button")
906
  chat_status = gr.Markdown(elem_id="chat-status")
907
  deeper_answer_button = gr.Button("Run deeper full-document answer", visible=False)
908
  deeper_answer_hint = gr.Markdown("")
tests/test_app.py CHANGED
@@ -1,4 +1,5 @@
1
- from app import _format_chat_entry, _stream_chat_entry
 
2
 
3
 
4
  def test_format_chat_entry_wraps_supporting_snippets_in_details() -> None:
@@ -24,3 +25,41 @@ def test_stream_chat_entry_appends_collapsible_snippets_after_text_stream() -> N
24
 
25
  assert frames[-2][-1]["content"] == "_Based on the summary and analysis._\n\nAnswer text"
26
  assert "<details><summary>Supporting snippet (1)</summary>" in frames[-1][-1]["content"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app import _empty_session, _format_chat_entry, _session_record, ask_question, _stream_chat_entry
2
+ from services.rag_pipeline import AnswerResult, Citation
3
 
4
 
5
  def test_format_chat_entry_wraps_supporting_snippets_in_details() -> None:
 
25
 
26
  assert frames[-2][-1]["content"] == "_Based on the summary and analysis._\n\nAnswer text"
27
  assert "<details><summary>Supporting snippet (1)</summary>" in frames[-1][-1]["content"]
28
+
29
+
30
+ def test_ask_question_uses_full_document_answers(monkeypatch) -> None:
31
+ session_state = _empty_session()
32
+ record = _session_record(session_state)
33
+ record.update(
34
+ {
35
+ "api_config": {"provider": "qwen", "api_key": "hf_test_token"},
36
+ "analysis": {},
37
+ "vector_store": object(),
38
+ "doc_text": "Full bill text",
39
+ "chat_history": [],
40
+ "pending_deeper_question": "old question",
41
+ }
42
+ )
43
+
44
+ monkeypatch.setattr("app.instantiate_client", lambda provider, api_key: object())
45
+
46
+ calls: list[tuple[object | None, str | None]] = []
47
+
48
+ def fake_answer_query_from_full_document(provider_client, vector_store, question, *, doc_text=None):
49
+ calls.append((vector_store, doc_text))
50
+ return AnswerResult(
51
+ answer="Full-document answer",
52
+ citations=[Citation(ref_id=1, snippet="Clause text")],
53
+ provenance="full_document",
54
+ )
55
+
56
+ monkeypatch.setattr("app.answer_query_from_full_document", fake_answer_query_from_full_document)
57
+
58
+ frames = list(ask_question("What does the bill require?", session_state, []))
59
+
60
+ assert calls == [(record["vector_store"], "Full bill text")]
61
+ assert "Reading the full document for an answer..." == frames[0][2]
62
+ assert "<details><summary>Supporting snippet (1)</summary>" in frames[-1][0][-1]["content"]
63
+ assert frames[-1][3]["visible"] is False
64
+ assert frames[-1][4] == ""
65
+ assert record["pending_deeper_question"] is None