Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[llm unify 2/n] Implement llm_map(_elements) and move extract_entity to it. #1126

Merged
merged 49 commits into from
Jan 30, 2025
Merged
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c2a8cfa
add prompt base classes and ElementListPrompt
HenryL27 Jan 17, 2025
21a115a
override .instead in ElementListPrompt to store net-new keys in self.…
HenryL27 Jan 17, 2025
f94da80
add ElementPrompt and StaticPrompt
HenryL27 Jan 17, 2025
b73c162
add unit tests for prompts
HenryL27 Jan 21, 2025
17b2163
forgot to commit this
HenryL27 Jan 21, 2025
5d145d5
address pr comments; flatten properties with flatten_data
HenryL27 Jan 21, 2025
7fa2ff1
support multiple user prompts
HenryL27 Jan 21, 2025
abf9b0b
rename instead to set
HenryL27 Jan 22, 2025
9909c7e
Merge branch 'main' of github.com:aryn-ai/sycamore into hml-llm-unify
HenryL27 Jan 22, 2025
2d1315b
add LLMMap and LLMMapElements transforms
HenryL27 Jan 22, 2025
1853d51
Merge branch 'main' of github.com:aryn-ai/sycamore into hml-llm-unify
HenryL27 Jan 22, 2025
5e86e56
move llm implementations to use RenderedPrompts
HenryL27 Jan 22, 2025
27581ef
also this guy
HenryL27 Jan 22, 2025
739b672
add docset methods
HenryL27 Jan 23, 2025
73d9bdd
docstrings
HenryL27 Jan 23, 2025
ed8785e
add llm_map unit tests
HenryL27 Jan 23, 2025
523d6e3
fix bedrock tests and chaching
HenryL27 Jan 23, 2025
e1b3206
fix anthropic and bedrock ITs
HenryL27 Jan 23, 2025
6500e1c
adjust caching to handle pydantic class response format properly
HenryL27 Jan 23, 2025
f50032d
fix base llm unit tests
HenryL27 Jan 23, 2025
c3c7ea8
adjust all testing mock llms to updated llm interface
HenryL27 Jan 23, 2025
ffaaf0f
deprecate extract entity and implement it with llm_map
HenryL27 Jan 24, 2025
d71cf1a
add context_params decorator to llm_map
HenryL27 Jan 24, 2025
4225e11
revert extract_entity docset method re-implementation
HenryL27 Jan 24, 2025
0d39b27
add initial support for prompts that generate a sequence of rendered …
HenryL27 Jan 25, 2025
0b5ded4
add stuff to EntityExtractor/OpenAIEntityExtractor to convert to LLMMap
HenryL27 Jan 25, 2025
a52f7c2
make docset.extract_entity construct an LLMMap from its entity_extractor
HenryL27 Jan 25, 2025
3a9ac3c
get extract entity working with tokenizer and token limit
HenryL27 Jan 28, 2025
befc3d0
get all extract_entity unit tests passing
HenryL27 Jan 28, 2025
8bf42d5
fix llm_map_elements to deal with postprocess index
HenryL27 Jan 28, 2025
d7ff1eb
add postprocess_fn unit tests for llm_map
HenryL27 Jan 28, 2025
a7a2cc0
ruff complaint
HenryL27 Jan 28, 2025
ebf721e
fix docset unittests
HenryL27 Jan 28, 2025
0bd2a45
move a bunch of stuff back to llm.generate_old. This includes the act…
HenryL27 Jan 28, 2025
95cbaaf
move more stuff back to llm.generate_old
HenryL27 Jan 28, 2025
ea7f0e6
fix the last few mocks
HenryL27 Jan 28, 2025
2e51ee1
Merge branch 'main' of github.com:aryn-ai/sycamore into hml-llm-unify
HenryL27 Jan 28, 2025
57a4e4b
ruff linelength
HenryL27 Jan 28, 2025
a312ba3
mypy!!!
HenryL27 Jan 28, 2025
ebde879
type: ignore + line length is tricky
HenryL27 Jan 28, 2025
ff5efdc
fix generate_old with SimplePrompts
HenryL27 Jan 28, 2025
370e2b7
set openai system role name to system instead of developer like their…
HenryL27 Jan 28, 2025
98ce6a0
address simple pr comments
HenryL27 Jan 29, 2025
1789409
pickle stuff in llm caching path bc not everything is jsonifiable
HenryL27 Jan 30, 2025
8b6f085
rewrite llm_map to deal with iterative prompting better
HenryL27 Jan 30, 2025
763acc5
add a b64encode-to-str to cache bc you can't put bytes in json either
HenryL27 Jan 30, 2025
0331866
fix llm its to mimic the _llm_cache_set/get pickle/unpickle operations
HenryL27 Jan 30, 2025
dfb7540
fix docstrings
HenryL27 Jan 30, 2025
f7c06e7
oops bad type signature
HenryL27 Jan 30, 2025
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
Prev Previous commit
Next Next commit
add postprocess_fn unit tests for llm_map
Signed-off-by: Henry Lindeman <hmlindeman@yahoo.com>
  • Loading branch information
HenryL27 committed Jan 28, 2025
commit d7ff1ebf46e22da762c94054c99855da71be9c35
48 changes: 46 additions & 2 deletions lib/sycamore/sycamore/tests/unit/transforms/test_base_llm.py
Original file line number Diff line number Diff line change
@@ -54,6 +54,23 @@ def test_happy_path(self):
assert outdocs[1].text_representation == "booga"
assert outdocs[1].properties["out"] == "booga"

def test_postprocess(self):
prompt = FakeDocPrompt()
llm = FakeLLM()
doc1 = Document({"text_representation": "ooga"})
doc2 = Document({"text_representation": "booga"})
count = 0

def ppfn(d: Document, i: int) -> Document:
nonlocal count
count += 1
return d

map = LLMMap(None, prompt, "out", llm, postprocess_fn=ppfn)
_ = map.llm_map([doc1, doc2])

assert count == 2


class TestLLMMapElements:
def test_wrong_prompt_fails_fast(self):
@@ -67,13 +84,40 @@ def test_happy_path(self):
prompt = FakeEltPrompt()
llm = FakeLLM()
doc1 = Document(
{"text_representation": "ooga", "elements": [{"text_representation": "yo"}, {"text_representation": "ho"}]}
{
"doc_id": "1",
"text_representation": "ooga",
"elements": [{"text_representation": "yo"}, {"text_representation": "ho"}],
}
)
doc2 = Document({"elements": [{"text_representation": "booga"}, {}]})
doc2 = Document({"doc_id": "2", "elements": [{"text_representation": "booga"}, {}]})
map = LLMMapElements(None, prompt, "out", llm)
outdocs = map.llm_map_elements([doc1, doc2])

assert outdocs[0].elements[0].properties["out"] == "oogayo"
assert outdocs[0].elements[1].properties["out"] == "oogaho"
assert outdocs[1].elements[0].properties["out"] == "Nonebooga"
assert outdocs[1].elements[1].properties["out"] == "NoneNone"

def test_postprocess(self):
prompt = FakeEltPrompt()
llm = FakeLLM()
doc1 = Document(
{
"doc_id": "1",
"text_representation": "ooga",
"elements": [{"text_representation": "yo"}, {"text_representation": "ho"}],
}
)
doc2 = Document({"doc_id": "2", "elements": [{"text_representation": "booga"}, {}]})
count = 0

def ppfn(e: Element, i: int) -> Element:
nonlocal count
count += 1
return e

map = LLMMapElements(None, prompt, "out", llm, postprocess_fn=ppfn)
_ = map.llm_map_elements([doc1, doc2])

assert count == 4
22 changes: 19 additions & 3 deletions lib/sycamore/sycamore/transforms/base_llm.py
Original file line number Diff line number Diff line change
@@ -119,6 +119,11 @@ class LLMMapElements(MapBatch):
llm: The llm to use for inference.
llm_mode: How to call the llm - sync/async/batch. All LLMs do not
necessarily implement all options.
postprocess_fn: function to call on documents after performing the
llm inference. If the prompt rendered into multiple RenderedPrompts,
``i`` is the index of the RenderedPrompt that succeeded; if the
prompt rendered into an empty list, ``i`` is -1; and otherwise
``i`` is 0

Example:
.. code-block:: python
@@ -138,22 +143,33 @@ def __init__(
output_field: str,
llm: LLM,
llm_mode: LLMMode = LLMMode.SYNC,
postprocess_fn: Callable[[Element, int], Element] = lambda e, i: e,
**kwargs,
):
self._prompt = prompt
self._validate_prompt()
self._output_field = output_field
self._llm = llm
self._llm_mode = llm_mode
self._postprocess_fn = postprocess_fn
super().__init__(child, f=self.llm_map_elements, **kwargs)

def llm_map_elements(self, documents: list[Document]) -> list[Document]:
rendered = [(e, self._prompt.render_element(e, d)) for d in documents for e in d.elements]
rendered = [(d, e, self._prompt.render_element(e, d)) for d in documents for e in d.elements]
results = _infer_prompts(
_as_sequences([p for _, p in rendered]), self._llm, self._llm_mode, self._prompt.is_done
_as_sequences([p for _, _, p in rendered]), self._llm, self._llm_mode, self._prompt.is_done
)
for (r, i), (e, _) in zip(results, rendered):
new_elts = []
last_doc = None
for (r, i), (d, e, _) in zip(results, rendered):
if last_doc is not None and last_doc.doc_id != d.doc_id:
last_doc.elements = new_elts
new_elts = []
e.properties[self._output_field] = r
new_elts.append(self._postprocess_fn(e, i))
last_doc = d
if last_doc is not None:
last_doc.elements = new_elts
return documents

def _validate_prompt(self):