GitHub Actions commited on
Commit
a09abbb
·
1 Parent(s): 5cae756

Deploy 698d58f

Browse files
app/pipeline/nodes/generate.py CHANGED
@@ -235,6 +235,15 @@ def _format_history(state: "PipelineState") -> str:
235
  return "Prior conversation (oldest first):\n" + "\n".join(lines) + "\n\n"
236
 
237
 
 
 
 
 
 
 
 
 
 
238
  def _merge_by_source(chunks: list) -> list[dict]:
239
  """
240
  Collapse chunks that share the same source_url + section (or source_title
@@ -448,6 +457,7 @@ def make_generate_node(llm_client: LLMClient, gemini_client=None) -> Callable[[P
448
  )
449
  )
450
  context_block_enum = "\n\n".join(enum_context_parts)
 
451
  prompt_enum = f"Items fetched from database:\n{context_block_enum}\n\nVisitor request: {query}"
452
  stream = llm_client.complete_with_complexity(
453
  prompt=prompt_enum,
@@ -497,6 +507,7 @@ def make_generate_node(llm_client: LLMClient, gemini_client=None) -> Callable[[P
497
  )
498
 
499
  context_block = "\n\n".join(context_parts)
 
500
 
501
  history_prefix = _format_history(state)
502
  is_criticism = state.get("is_criticism", False)
 
235
  return "Prior conversation (oldest first):\n" + "\n".join(lines) + "\n\n"
236
 
237
 
238
+ def _numbered_sources_event(source_refs: list[SourceRef]) -> dict:
239
+ """
240
+ The sources the streamed answer's [n] markers count from 1 in, sent before its first token.
241
+ Retrieval's sources event lists chunks before they are merged by document, and the done
242
+ event's list is renumbered for the stored answer, so neither matches the streamed words.
243
+ """
244
+ return {"type": "sources", "sources": [ref.model_dump() for ref in source_refs]}
245
+
246
+
247
  def _merge_by_source(chunks: list) -> list[dict]:
248
  """
249
  Collapse chunks that share the same source_url + section (or source_title
 
457
  )
458
  )
459
  context_block_enum = "\n\n".join(enum_context_parts)
460
+ writer(_numbered_sources_event(source_refs))
461
  prompt_enum = f"Items fetched from database:\n{context_block_enum}\n\nVisitor request: {query}"
462
  stream = llm_client.complete_with_complexity(
463
  prompt=prompt_enum,
 
507
  )
508
 
509
  context_block = "\n\n".join(context_parts)
510
+ writer(_numbered_sources_event(source_refs))
511
 
512
  history_prefix = _format_history(state)
513
  is_criticism = state.get("is_criticism", False)
tests/test_generate_numbered_sources.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from unittest.mock import patch
2
+
3
+ import pytest
4
+
5
+ from app.pipeline.nodes.generate import make_generate_node
6
+
7
+
8
+ def _chunk(title: str, url: str, text: str) -> dict:
9
+ return {
10
+ "text": text,
11
+ "metadata": {"source_title": title, "source_url": url, "section": "Overview", "source_type": "github_readme", "doc_id": title.lower(), "chunk_index": 0},
12
+ }
13
+
14
+
15
+ class _StreamingLLM:
16
+ suggests_follow_ups = True
17
+
18
+ def __init__(self) -> None:
19
+ self.prompt = ""
20
+
21
+ async def complete_with_complexity(self, prompt, system, stream, complexity):
22
+ self.prompt = prompt
23
+ yield "Cortex maps a codebase into a graph [2]."
24
+
25
+
26
+ @pytest.mark.asyncio
27
+ async def test_streamed_citations_count_in_the_sources_sent_before_the_answer():
28
+ # Two chunks of one document share a number in the prompt, so Cortex is [2], not [3].
29
+ chunks = [
30
+ _chunk("AimBuddy", "https://github.com/1337Xcode/AimBuddy", "AimBuddy tracks targets on Android in real time."),
31
+ _chunk("AimBuddy", "https://github.com/1337Xcode/AimBuddy", "It runs a YOLO model on the phone's GPU through NCNN."),
32
+ _chunk("Cortex", "https://github.com/1337Xcode/Cortex", "Cortex maps a codebase into a SQLite graph for coding agents."),
33
+ ]
34
+ events: list[dict] = []
35
+ llm = _StreamingLLM()
36
+
37
+ with patch("app.pipeline.nodes.generate.get_stream_writer", return_value=events.append):
38
+ await make_generate_node(llm)({"query": "What is Cortex?", "reranked_chunks": chunks, "query_complexity": "simple"})
39
+
40
+ kinds = [event["type"] for event in events]
41
+ [sources] = [event["sources"] for event in events if event["type"] == "sources"]
42
+ assert [source["title"] for source in sources] == ["AimBuddy", "Cortex"]
43
+ assert "[2] Cortex" in llm.prompt
44
+ assert kinds.index("sources") < kinds.index("token")
tests/test_site_profile.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ ROOT = Path(__file__).resolve().parents[2]
5
+ if str(ROOT) not in sys.path:
6
+ sys.path.insert(0, str(ROOT))
7
+
8
+ from ingestion.site_profile import profile_documents
9
+
10
+ LLMS_TXT = """# Darshan Chheda
11
+
12
+ > London-based software engineer.
13
+
14
+ ## Availability
15
+ Open to software engineering roles in the UK
16
+
17
+ ## Links
18
+ - [Website](https://www.darshanchheda.com): Canonical portfolio and profile
19
+ - [Email](mailto:me@darshanchheda.com): Direct contact
20
+ - [GitHub](https://github.com/1337XCode)
21
+ - [LinkedIn](https://linkedin.com/in/darshchheda)
22
+
23
+ ## Pages
24
+ - [Photos](https://www.darshanchheda.com/photos): Photographs.
25
+ """
26
+
27
+
28
+ def test_the_page_is_one_profile_and_each_off_site_profile_its_own_document() -> None:
29
+ documents = profile_documents(LLMS_TXT)
30
+
31
+ assert [(d["title"], d["url"], d["slug"]) for d in documents] == [
32
+ ("darshanchheda.com", "https://www.darshanchheda.com/", "site-profile"),
33
+ ("GitHub", "https://github.com/1337XCode", "profile-github"),
34
+ ("LinkedIn", "https://linkedin.com/in/darshchheda", "profile-linkedin"),
35
+ ]
36
+ assert "Open to software engineering roles in the UK" in documents[0]["clean_content"]
37
+ assert documents[2]["clean_content"].endswith("https://linkedin.com/in/darshchheda")
38
+ assert len(documents[2]["clean_content"].split()) >= 8