-
Notifications
You must be signed in to change notification settings - Fork 80
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
[python] Use OutputDataWithValue type for function calls #611
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we're better off keeping strings just as strings. Thoughts?
...ngFace/python/src/aiconfig_extension_hugging_face/remote_inference_client/text_generation.py
Show resolved
Hide resolved
bf687d2
to
8d3e926
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few blocking comments, but otherwise looks good. Really nice work! Not an easy change
# Gemini does not support function calls so shouldn't | ||
# get here, but just being safe | ||
return json.dumps(output_data.value, indent=2) | ||
raise ValueError("Not Implemented") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove? Or update string with a more descriptive error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should never actually get here but yea I can remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: Ok I can't remove, I added more detailed error message so it's clear why
accumulated_message += new_text | ||
options.stream_callback(new_text, accumulated_message, 0) | ||
output.data = accumulated_message | ||
if isinstance(new_text, str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the case where new_text
isn't str
? Can that happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: It's actually possible (though practically unlikely), see comments in #611 (comment). I get what you're saying (this change seems unnecessary and more confusing) so I'll remove this type of logic from this diff
No it can't happen for hugging face text generation model parser
# HuggingFace Text generation does not support function | ||
# calls so shouldn't get here, but just being safe | ||
return json.dumps(output_data.value, indent=2) | ||
raise ValueError("Not Implemented") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: update string comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can remove the value error, but why remove the comment?
...ngFace/python/src/aiconfig_extension_hugging_face/remote_inference_client/text_generation.py
Outdated
Show resolved
Hide resolved
extensions/LLama-Guard/python/python/src/aiconfig_extension_llama_guard/LLamaGuard.py
Outdated
Show resolved
Hide resolved
role = output.metadata.get("assistant", None) or \ | ||
("rawResponse" in output.metadata and | ||
output.metadata["rawResponse"].get("assistant", None)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect. Can you check if TS or Python implementations have this issue elsewhere in your stack?
role = output.metadata.get("assistant", None) or \ | |
("rawResponse" in output.metadata and | |
output.metadata["rawResponse"].get("assistant", None)) | |
role = output.metadata.get("role", None) or \ | |
("rawResponse" in output.metadata and | |
output.metadata["rawResponse"].get("role", None)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also perhaps we should default role to "assistant"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, just copy pasted wrong my bad, should be role
.
Can you check if TS or Python implementations have this issue elsewhere in your stack?
I don't have this issue anywhere else (https://github.com/lastmile-ai/aiconfig/pull/610/files#diff-e044912cdb054134cac74868d76969daa92c6dd7c1238805672e023f9b7aa92d), this was just a typo
Also perhaps we should default role to "assistant"
We shouldn't be doing this. Role can be one of 5 different types, not just "assistant", so we need to check for that explicitly: https://github.com/openai/openai-node/blob/b595cd953a704dba2aef4c6c3fa431f83f18ccf9/src/resources/chat/completions.ts#L514
elif isinstance(output_data, OutputDataWithValue): | ||
if isinstance(output_data.value, str): | ||
content = output_data.value | ||
elif output_data.kind == "tool_calls": | ||
assert isinstance(output, OutputDataWithToolCallsValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this can be simplified:
if isinstance(output_data, str):
# Do string stuff
elif isinstance(output_data, OutputDataWithToolCallsValue):
# Do function call stuff
elif isinstance(output_data, OutputDataWithVale):
# Do other value stuff where data.value is string
elif isinstance(output_data, ChatCompletionMessage):
# Do chat completion stuff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this more complicated and unintuitive. OutputDataWithToolCallsValue
is a subset of OutputDataWithValue
. I get what you're saying that you can have single else-if instead of nested if-statements, but it's jarring to go from "small subset" --> "bigger subset" while reading down through if-else logic.
This is also more confusing because the inner # Do string stuff
, # Do function call stuff
etc all need to individually check for the role type being assistant before we can do anything
if message.get("content", None) is not None: | ||
output_data = message.get("content") | ||
elif message.get("tool_calls", None) is not None: | ||
tool_calls = [ToolCallData( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this is fine for now, but we might need to update this in the future as tool_calls
include things that aren't just function calls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good callout, I'll do another check where I'll check for the type being 'function'
accumulated_message += new_text | ||
options.stream_callback(new_text, accumulated_message, 0) | ||
output.data = accumulated_message | ||
if isinstance(new_text, str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be anything other than string? These changes seem unnecessary if I'm understanding the streamer correctly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Streamer can actually contain anything, doesn't have to be text. Theoretically it always be str (except for the stop signal), but I'm just being safe. This is a fork of the HF source code (couldn't find it publicly on GH, but it's this: https://github.com/PaddlePaddle/PaddleNLP/blob/a55039cc16bbcfa06b204a5f21b813e98a3f65ca/paddlenlp/generation/streamers.py#L197-L206)
# HuggingFace Text generation does not support function | ||
# calls so shouldn't get here, but just being safe | ||
return json.dumps(output_data.value, indent=2) | ||
raise ValueError("Not Implemented") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these cases we could technically json.dumps(output_data), right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should never be possible to get to this point, which is why I raised an error to be cautious. But yea I can just remove error and fall into the final return ""
instead
# If response_includes_details is false, `iteration` will be a string, | ||
# otherwise, `iteration` is a TextGenerationStreamResponse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we update response
type to Iterable[Union[TextGenerationStreamResponse, str]]
to reflect this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extensions/LLama-Guard/python/python/src/aiconfig_extension_llama_guard/LLamaGuard.py
Outdated
Show resolved
Hide resolved
extensions/LLama-Guard/python/python/src/aiconfig_extension_llama_guard/LLamaGuard.py
Outdated
Show resolved
Hide resolved
For testing, can you please be sure to run the tests and the relevant cookbooks/demo scripts (e.g. to test function calling) |
Update: Created issue to track this in #659
I ran the automated tests and function calls still work (notably the I get what you're saying just to test the relevant cookbook, so this below isn't to disagree with your comment, just sharing general thoughts to flag: I would be surprised if all the cookbooks still worked after these output refactoring PRs from last few days, since we're changing the way we output data and thus changing how we should access it. I feel having automated testing for the cookbooks should be P0 after we finish local editor MVP. I've mentioned to Sarmad that it's simply not feasible to test them all manually. If any of them are broken now, to me having automated testing + fixing them is a separate work-project |
This is extending #605 where I converted from model-specific response types --> pure strings. Now I'm going to also support function calls --> OutputData types Same as last diff (#610) except now for python files instead of typescript Don't need to do this for the image outputs since I already did that in #608 Actually like typescript, the only model parser we need to support for function calling is `openai.py`
See comment here for context: #611 (comment) Separating this diff from #611 to make it easier and get that one unblocked
See comment here for context: #611 (comment) Separating this diff from #611 to make it easier and get that one unblocked
Explicit str type checking in model parsers See comment here for context: #611 (comment) Separating this diff from #611 to make it easier and get that one unblocked
See comment here for context: lastmile-ai#611 (comment) Separating this diff from lastmile-ai#611 to make it easier and get that one unblocked
[python] Use OutputDataWithValue type for function calls
This is extending #605 where I converted from model-specific response types --> pure strings. Now I'm going to also support function calls --> OutputData types
Same as last diff (#610) except now for python files instead of typescript
Don't need to do this for the image outputs since I already did that in #608
Actually like typescript, the only model parser we need to support for function calling is
openai.py