Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
920037c
TimedEntry.time is now publicly accessible
ppinchuk Aug 26, 2026
871e4b1
time prop no longer writable
ppinchuk Aug 26, 2026
100ffda
Entry value no longer writable
ppinchuk Aug 26, 2026
c2f103b
Adding an entry now returns its time
ppinchuk Aug 26, 2026
73314c4
Base LLM service now accepts a rate stats tracker
ppinchuk Aug 26, 2026
32bc6be
`OpenAIService` now accepts a rate stats tracker
ppinchuk Aug 26, 2026
8e82c29
`OpenAIService` now tracks rate stats
ppinchuk Aug 26, 2026
f6bf239
`UsageUpdater` now takes two inputs to call method
ppinchuk Aug 26, 2026
dcd07f3
Docs
ppinchuk Aug 26, 2026
89aad85
Service now accepts a rate tracker
ppinchuk Aug 26, 2026
c67462c
Update param name
ppinchuk Aug 26, 2026
5437e6b
Add rate tracker
ppinchuk Aug 26, 2026
9c5da25
Extraction plugin keeps a rate tracker
ppinchuk Aug 26, 2026
d71851d
`LLMRateTracker` no longer need to know models ahead of time
ppinchuk Aug 26, 2026
d00e8ab
`BaseRequest` carries a rate tracker
ppinchuk Aug 26, 2026
a87ec75
Fix directive
ppinchuk Aug 26, 2026
3176eec
Make tracker read only
ppinchuk Aug 26, 2026
2a622b3
Runtime exposes tracker
ppinchuk Aug 26, 2026
634d964
llm_usage_rates now written to meta
ppinchuk Aug 26, 2026
369c6cb
Pass through rate info to meta
ppinchuk Aug 26, 2026
840f225
Attempt to recover rates from partial run
ppinchuk Aug 26, 2026
8af45e5
Add/fix tests
ppinchuk Aug 26, 2026
ba1f263
Add concurrent request tracking
ppinchuk Aug 26, 2026
a88c7fc
Rename class
ppinchuk Aug 26, 2026
5dc63e4
Remove bad input
ppinchuk Aug 26, 2026
e6b6e46
Pop non-serializable arg
ppinchuk Aug 27, 2026
dc401c8
Fix input for llm call
ppinchuk Aug 27, 2026
8928755
Fix
ppinchuk Aug 27, 2026
9dbd3db
Merge remote-tracking branch 'origin/main' into pp/more_runtime_stats
ppinchuk Aug 27, 2026
c2f3822
Fix tests
ppinchuk Aug 27, 2026
280a937
Fix integration tests
ppinchuk Aug 28, 2026
fd8b385
Keep as dict while ranking
ppinchuk Aug 28, 2026
cc0acd6
Add search engines attr if available
ppinchuk Aug 28, 2026
424114f
Store an overall search engines attr
ppinchuk Aug 28, 2026
ef0a323
Add tests
ppinchuk Aug 28, 2026
d00e9dd
update elm dep
ppinchuk Aug 28, 2026
10566b7
Update to use new elm functionality
ppinchuk Aug 28, 2026
fb36e70
Add output
ppinchuk Aug 28, 2026
b76fa66
Move key
ppinchuk Aug 28, 2026
f9a6a2d
Add info retrieval method
ppinchuk Aug 28, 2026
d0f1c5b
Add doc from steps to extracted doc
ppinchuk Aug 28, 2026
371f0b6
Update tests
ppinchuk Aug 28, 2026
56f914a
MInor refactor
ppinchuk Aug 28, 2026
088cca5
Rename var for less verbosity
ppinchuk Aug 28, 2026
d2cd2be
Merge remote-tracking branch 'origin/main' into pp/more_runtime_stats
ppinchuk Aug 30, 2026
e3efbfd
Merge branch 'pp/more_runtime_stats' into pp/se_in_attrs
ppinchuk Aug 30, 2026
704f635
Minor doc changes
ppinchuk Aug 30, 2026
f7785e8
Merge remote-tracking branch 'origin/main' into pp/se_in_attrs
ppinchuk Aug 30, 2026
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
16 changes: 11 additions & 5 deletions compass/pipeline/collection/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,11 @@ async def execute(self, *, eager_extract=False):
for step in self._unfinished_steps():
docs = await self._run_collection_step(step)
if eager_extract:
context = (
await self.workflow.extraction_workflow.extract_from_docs(
docs
)
context = await self.workflow.extraction.extract_from_docs(
docs
)
if context is not None:
return context
return self._context_with_documented_steps(context)
else:
self._collection_info = (
await self.workflow.write_collection_shard_no_fail(
Expand Down Expand Up @@ -182,6 +180,14 @@ async def _run_collection_step(self, step):
self._completed_steps.add(step.STEP_NAME)
return docs

def _context_with_documented_steps(self, context):
"""Attach collection steps to each document in the context"""
for doc in context.data_docs:
doc.attrs["from_steps"] = list(
self.de_duplicator.info(doc).from_steps
)
return context

def _log_execute_results(self):
"""Log the results of the collection execution"""
if self.de_duplicator:
Expand Down
17 changes: 17 additions & 0 deletions compass/pipeline/collection/dedupe.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ def add_docs(self, docs, *, step_name=None):
doc_info = self.data.setdefault(key, _DocInfo.from_doc(doc))
doc_info.add_step(step_name)

def info(self, doc):
"""Get the info for a given document

Parameters
----------
doc : BaseDocument
The document for which to retrieve the deduplication info.

Returns
-------
object
The deduplication info for the given document.
"""
return self.data.get(
_collection_doc_key(doc.attrs), _DocInfo(doc=doc, from_steps=[])
)


def _collection_doc_key(doc_info):
"""Build the deduplication key for a collected document"""
Expand Down
16 changes: 6 additions & 10 deletions compass/pipeline/jurisdiction.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ def __init__(
self.perform_website_search = perform_website_search
self.jurisdiction_website = jurisdiction.website_url
self.last_scrape_results = []
self.extraction_workflow = DocumentExtraction(self)
self.collection_workflow = DocumentCollection(self)
self.collection = DocumentCollection(self)
self.extraction = DocumentExtraction(self)

async def process(self):
"""Run process mode for one jurisdiction
Expand All @@ -101,8 +101,8 @@ async def process(self):
self.jurisdiction.code,
)
try:
extraction_context = await self.collection_workflow.execute(
eager_extract=True,
extraction_context = await self.collection.execute(
eager_extract=True
)
finally:
await self.extractor.record_usage()
Expand Down Expand Up @@ -143,9 +143,7 @@ async def collect(self):
self.jurisdiction.full_name,
)

collection_info = await self.collection_workflow.execute(
eager_extract=False
)
collection_info = await self.collection.execute(eager_extract=False)

logger.info(
"Completed collection for jurisdiction: %s",
Expand Down Expand Up @@ -187,9 +185,7 @@ async def extract_from_collection_info(self, collection_info):
collection_info, task_name=self.jurisdiction.full_name
)
docs = [doc for doc in docs if doc is not None]
extraction_context = (
await self.extraction_workflow.extract_from_docs(docs)
)
extraction_context = await self.extraction.extract_from_docs(docs)
finally:
await self.extractor.record_usage()
await _record_jurisdiction_info(
Expand Down
18 changes: 13 additions & 5 deletions compass/scripts/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,21 +767,29 @@ async def _docs_from_web_search(
**kwargs,
)
ranked_results = {
res.get("url"): res.get("overall_rank") or 1
res.get("url"): res
for res in out["results"]
if res.get("filtered_reason") is None and res.get("url") is not None
}
urls = sorted(ranked_results, key=ranked_results.get)
urls = sorted(
ranked_results,
key=lambda url: ranked_results[url].get("overall_rank") or 1,
)
if not urls:
return []

docs = await _docs_from_urls(
urls, jurisdiction.full_name, browser_semaphore, **kwargs
)
for doc in docs:
doc.attrs[_COLLECTION_SCORE_KEY] = ranked_results.get(
doc.attrs.get("source")
)
result = ranked_results.get(doc.attrs.get("source"))
if result is None:
doc.attrs[_COLLECTION_SCORE_KEY] = None
continue

doc.attrs[_COLLECTION_SCORE_KEY] = result.get("overall_rank") or 1
if "search_engines" in result:
doc.attrs["search_engines"] = list(result["search_engines"])
return docs


Expand Down
6 changes: 3 additions & 3 deletions compass/services/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def __init__(
Name of model being used.
rate_limit : int or float, optional
Token rate limit (typically per minute, but the time
interval is ultimately controlled by the `rate_tracker`
interval is ultimately controlled by the `timed_tracker`
instance). By default, ``1e3``.
timed_tracker : TimeBoundedUsageTracker, optional
Instance used to track usage per time interval and compare
Expand Down Expand Up @@ -186,7 +186,7 @@ async def process(
return _get_response_message(response)

def _record_prompt_tokens(self, kwargs):
"""Add prompt token count to rate tracker"""
"""Add prompt token count to timed tracker"""
num_tokens = count_tokens(kwargs.get("messages", []), self.model_name)
return self.timed_tracker.add(num_tokens)

Expand All @@ -197,7 +197,7 @@ def _record_request_rate(self, timestamp, rate_tracker):
rate_tracker.record_request(self.model_name, timestamp)

def _record_completion_tokens(self, response):
"""Add completion token count to rate tracker"""
"""Add completion token count to timed tracker"""
if response is None:
return None
return self.timed_tracker.add(response.usage.completion_tokens)
Expand Down
1 change: 1 addition & 0 deletions compass/services/threaded.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ def _compile_doc_info(doc):
"permitted_use_text_ngram_score"
),
"from_steps": doc.attrs.get("from_steps"),
"search_engines": doc.attrs.get("search_engines"),
}


Expand Down
2 changes: 1 addition & 1 deletion compass/utilities/costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def compute_cost_from_totals(totals):
should contain "prompt_tokens" and "response_tokens" keys
indicating the number of tokens used for prompts and responses,
respectively. This dictionary is typically obtained from the
`tracker_totals` property of a LLMUsageTracker instance.
`tracker_totals` entry of a LLMUsageTracker instance.

Returns
-------
Expand Down
10 changes: 7 additions & 3 deletions compass/web/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
from urllib.parse import urlsplit, urlunsplit

from elm.web.search.run import search_with_fallback, search_all_se
from elm.web.search.run import search_all_se, search_with_fallback_with_attrs

from compass.utilities.url import URLPartFilter

Expand Down Expand Up @@ -150,7 +150,7 @@ async def _run_simple_sort_search(
**se_kwargs,
):
"""Run search with fallback search engines, applying simple sort"""
urls = await search_with_fallback(
return await search_with_fallback_with_attrs(
queries,
num_urls=num_urls,
url_ignore_substrings=url_ignore_substrings,
Expand All @@ -159,7 +159,6 @@ async def _run_simple_sort_search(
task_name=jurisdiction_full_name,
**se_kwargs,
)
return [{"url": url} for url in urls]


async def _run_holistic_sort_search(
Expand Down Expand Up @@ -236,6 +235,9 @@ def _apply_duplicate_filters(results):
config only wins among entries that are otherwise tied (same
``query_rank`` and ``query_index``).
"""
for entry in results:
entry["search_engines"] = [entry["search_engine"]]

winners = {}
for entry in _active_results_sorted(results):
key = entry["url"]
Expand All @@ -252,6 +254,8 @@ def _apply_duplicate_filters(results):
"query_rank": entry["query_rank"],
}
)
if entry["search_engine"] not in winner["search_engines"]:
winner["search_engines"].append(entry["search_engine"])

entry["filtered_reason"] = "duplicate"

Expand Down
6 changes: 5 additions & 1 deletion examples/execution_basics/config_kitchen_sink.json5
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
// futures to sit idly, awaiting their turn to query the LLM
"max_num_concurrent_jurisdictions": 25,
"url_ignore_substrings": [
// ignore any URLs with these strings
// ignore search results and COMPASS crawl URLs with these strings
// useful to filter out known noise
"wiki",
"nrel.gov",
Expand All @@ -94,6 +94,10 @@
// e.g. a giant file with generic ordinances that should be skipped
"www.co.delaware.in.us/egov/documents/1649699794_0382.pdf",
],
"url_keep_substrings": [
// override default and configured URL exclusions in both phases
"my_ordinance_collection.edu",
],
"known_doc_urls": {
"18017": "https://www.in.gov/counties/cass/files/Cass-County-Zoning-Ordinance-2024.pdf",
// Can include more FIPS -> URL mappings here
Expand Down
Loading