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

Python: unhashable type exception for custom return type in kernel function #10299

Closed
abhirockzz opened this issue Jan 27, 2025 · 1 comment · Fixed by #10316
Closed

Python: unhashable type exception for custom return type in kernel function #10299

abhirockzz opened this issue Jan 27, 2025 · 1 comment · Fixed by #10316
Assignees
Labels
bug Something isn't working python Pull requests for the Python Semantic Kernel

Comments

@abhirockzz
Copy link

Describe the bug

TypeError: unhashable type: 'AccountBalance'

I am using a kernel function that returns a custom type:

    @kernel_function(description="Get user account balance")
    async def get_user_account_balance(self, account_id: str) -> "AccountBalance":
        //....
        return AccountBalance(account_balance=account_balance)

    class AccountBalance(BaseModel):
          balance: int = Field(..., alias="account_balance")

To Reproduce
Steps to reproduce the behavior:

  1. Create an agent group chat
  2. Add an agent to the group chat with a kernel function that returns a custom type such as AccountBalance
  3. Invoke the agent through the group chat and let the agent invoke the kernel function that returns a list
  4. See error

Expected behavior
Custom types in kernel function should work

Screenshots
If applicable, add screenshots to help explain your problem.

Platform

  • Language: Python
  • Source: 1.19.0
  • IDE: VS Code]
  • OS: Mac

Additional context

  File "/demo/.venv/lib/python3.12/site-packages/semantic_kernel/agents/group_chat/agent_group_chat.py", line 144, in invoke
    async for message in super().invoke_agent(selected_agent):
  File "/demo/.venv/lib/python3.12/site-packages/semantic_kernel/agents/group_chat/agent_chat.py", line 144, in invoke_agent
    async for is_visible, message in channel.invoke(agent):
  File "/demo/.venv/lib/python3.12/site-packages/semantic_kernel/agents/channels/chat_history_channel.py", line 75, in invoke
    mutated_history.add(mutated_message)
  File "/demo/.venv/lib/python3.12/site-packages/semantic_kernel/contents/chat_message_content.py", line 318, in __hash__
    return hash((self.tag, self.role, self.content, self.encoding, self.finish_reason, *self.items))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/demo/.venv/lib/python3.12/site-packages/semantic_kernel/contents/function_result_content.py", line 197, in __hash__
    return hash((
           ^^^^^^
TypeError: unhashable type: 'AccountBalance'
@abhirockzz abhirockzz added the bug Something isn't working label Jan 27, 2025
@markwallace-microsoft markwallace-microsoft added python Pull requests for the Python Semantic Kernel triage labels Jan 27, 2025
@moonbox3
Copy link
Contributor

Hi @abhirockzz, we'll improve the handling for types so the hash doesn't error. To unblock you right now, I'd advise that you make you freeze your BaseModel like:

class AccountBalance(BaseModel):
    # Make the model frozen so it's hashable
    balance: int = Field(..., alias="account_balance")
    model_config = ConfigDict(frozen=True)

I tested this with:

def test_hash_with_frozen_account_balance():
    balance = AccountBalance(account_balance=100)
    content = FunctionResultContent(
        id="test_id",
        result=balance,
        function_name="TestFunction",
    )
    # Verify we can call hash without error
    _ = hash(content)
    assert True, "Hashing FunctionResultContent with frozen model should not raise errors."

and this passes. That would be the quickest way to handle it now while we make some improvements. Thanks for filing the issue.

github-merge-queue bot pushed a commit that referenced this issue Jan 28, 2025
…ndling FRC result so it can be hashed. (#10316)

### Motivation and Context

Two topics in this PR:
- A request to be able to pass in Enums to the Python process
`on_function_result`, instead of just strings.
- There were some custom types (Pydantic models) that were unable to
hash during an agent group chat. One could mark the model `frozen` so
that it is hashable; however, we should add better handling in our code,
in case a dev doesn't want to do that.

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

### Description

This PR:
- checks if the function name in `on_function_result` in our process
framework is an enum, if so, convert to a string.
- improves the parsing of the `result` in `FunctionResultContent` so
that it can be included in the hash function during an agent group chat.
Adding tests to handle multiple different scenarios.
- Closes #10299 
- Closes #10305

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [X] The code builds clean without any errors or warnings
- [X] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [X] All unit tests pass, and I have added new tests where possible
- [X] I didn't break anyone 😄
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working python Pull requests for the Python Semantic Kernel
Projects
None yet
5 participants