Skip to content

Chore/type checking types 01 #105

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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

CasperGN
Copy link
Contributor

@CasperGN CasperGN commented Apr 26, 2025

First iteration of fixing type errors reported by mypy for dapr_agents.types:

before

tox -e type
type: commands[0] quickstarts/05-multi-agent-workflow-dapr-workflows> mypy --config-file mypy.ini
dapr_agents/types/agent.py:73: error: Argument "default_factory" to "Field" has incompatible type "type[list[_T]]"; expected "Callable[[], Never] | Callable[[dict[str, Any]], Never]"  [arg-type]
dapr_agents/types/agent.py:79: error: Argument "default_factory" to "Field" has incompatible type "type[list[_T]]"; expected "Callable[[], Never] | Callable[[dict[str, Any]], Never]"  [arg-type]
dapr_agents/types/message.py:190: error: Incompatible return value type (got "dict[str, Any] | None", expected "str | None")  [return-value]
dapr_agents/types/message.py:213: error: "str" has no attribute "get"  [attr-defined]
dapr_agents/types/llm.py:146: error: Incompatible types in assignment (expression has type "None", variable has type "str")  [assignment]
dapr_agents/types/llm.py:159: error: Incompatible types in assignment (expression has type "None", variable has type "str")  [assignment]
dapr_agents/types/llm.py:168: error: Incompatible types in assignment (expression has type "None", variable has type "str")  [assignment]
dapr_agents/types/llm.py:428: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "model_fields_set"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "dict[Any, Any]" of "OpenAIChatCompletionParams | HFHubChatCompletionParams | NVIDIAChatCompletionParams | dict[Any, Any] | Any" has no attribute "model"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "AzureOpenAIModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "name"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "name"  [union-attr]
dapr_agents/types/llm.py:430: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "model_fields_set"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "dict[Any, Any]" of "OpenAIChatCompletionParams | HFHubChatCompletionParams | NVIDIAChatCompletionParams | dict[Any, Any] | Any" has no attribute "model"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "OpenAIModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "HFHubModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
Found 19 errors in 3 files (checked 165 source files)
dapr_agents/types/llm.py:431: error: Item "NVIDIAModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:553: error: Item "tuple[Any, ...]" of "BufferedReader | IO[Any] | tuple[Any, ...]" has no attribute "closed"  [union-attr]
dapr_agents/types/llm.py:617: error: Item "tuple[Any, ...]" of "BufferedReader | IO[Any] | tuple[Any, ...]" has no attribute "closed"  [union-attr]
type: exit 1 (2.27 seconds) /Users/scni/workspace/dapr-agents> mypy --config-file mypy.ini pid=98092
  type: FAIL code 1 (2.29=setup[0.03]+cmd[2.27] seconds)
  evaluation failed :( (2.33 seconds)

Now

tox -e type
type: commands[0] quickstarts/05-multi-agent-workflow-dapr-workflows> mypy --config-file mypy.ini
Success: no issues found in 165 source files
  type: OK (2.16=setup[0.03]+cmd[2.13] seconds)
  congratulations :) (2.20 seconds)

Remaining
None.

@yaron2, @Cyb3rWard0g: How do you best want to deal with the union-attr error where some of the parameters.model will not contain all possible configurations?

EDIT:
Updated with the cast as per @Cyb3rWard0g . Now we're down to 0 errors. Updated above.

@CasperGN
Copy link
Contributor Author

Chipping off the remaining work in #30

@@ -183,7 +184,7 @@ class ChatCompletion(BaseModel):
object: Optional[str] = None
usage: dict

def get_message(self) -> Optional[str]:
def get_message(self) -> Optional[Dict[str, Any]]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think this is correct because getting Content is just a plain string right?

def get_content(self) -> Optional[str]:
        """
        Retrieve the content from the first choice's message.
        """
        message = self.get_message()
        return message.get("content") if message else None

A Response Example -> Choices -> 0 -> Message -> Content right?

{
  "id": "chatcmpl-B9MBs8CjcvOU2jLn4n570S5qMJKcT",
  "object": "chat.completion",
  "created": 1741569952,
  "model": "gpt-4.1-2025-04-14",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I assist you today?",
        "refusal": null,
        "annotations": []
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 19,
    "completion_tokens": 10,
    "total_tokens": 29,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "service_tier": "default"
}

So having Opetional[str] is the right thing here and no Optional[Dict[str, Any]] right?

Copy link
Contributor Author

@CasperGN CasperGN Apr 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Cyb3rWard0g yes, the content from get_content returns a plain string howeveer get_message returns from .model_dump() which returns a Dict[str, Any] :

    def model_dump(
        self,
        *,
        mode: Literal['json', 'python'] | str = 'python',
        include: IncEx | None = None,
        exclude: IncEx | None = None,
        context: Any | None = None,
        by_alias: bool | None = None,
        exclude_unset: bool = False,
        exclude_defaults: bool = False,
        exclude_none: bool = False,
        round_trip: bool = False,
        warnings: bool | Literal['none', 'warn', 'error'] = True,
        fallback: Callable[[Any], Any] | None = None,
        serialize_as_any: bool = False,
    ) -> dict[str, Any]:
...

This then also ensures that when get_content is fetched (which calls get_message) the .get("content") works since it's a dict (well, unless a None was returned).

@Cyb3rWard0g
Copy link
Collaborator

First iteration of fixing type errors reported by mypy for dapr_agents.types:

before

tox -e type
type: commands[0] quickstarts/05-multi-agent-workflow-dapr-workflows> mypy --config-file mypy.ini
dapr_agents/types/agent.py:73: error: Argument "default_factory" to "Field" has incompatible type "type[list[_T]]"; expected "Callable[[], Never] | Callable[[dict[str, Any]], Never]"  [arg-type]
dapr_agents/types/agent.py:79: error: Argument "default_factory" to "Field" has incompatible type "type[list[_T]]"; expected "Callable[[], Never] | Callable[[dict[str, Any]], Never]"  [arg-type]
dapr_agents/types/message.py:190: error: Incompatible return value type (got "dict[str, Any] | None", expected "str | None")  [return-value]
dapr_agents/types/message.py:213: error: "str" has no attribute "get"  [attr-defined]
dapr_agents/types/llm.py:146: error: Incompatible types in assignment (expression has type "None", variable has type "str")  [assignment]
dapr_agents/types/llm.py:159: error: Incompatible types in assignment (expression has type "None", variable has type "str")  [assignment]
dapr_agents/types/llm.py:168: error: Incompatible types in assignment (expression has type "None", variable has type "str")  [assignment]
dapr_agents/types/llm.py:428: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "model_fields_set"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "dict[Any, Any]" of "OpenAIChatCompletionParams | HFHubChatCompletionParams | NVIDIAChatCompletionParams | dict[Any, Any] | Any" has no attribute "model"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "AzureOpenAIModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "name"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "name"  [union-attr]
dapr_agents/types/llm.py:430: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "model_fields_set"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "dict[Any, Any]" of "OpenAIChatCompletionParams | HFHubChatCompletionParams | NVIDIAChatCompletionParams | dict[Any, Any] | Any" has no attribute "model"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "OpenAIModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "HFHubModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
Found 19 errors in 3 files (checked 165 source files)
dapr_agents/types/llm.py:431: error: Item "NVIDIAModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:553: error: Item "tuple[Any, ...]" of "BufferedReader | IO[Any] | tuple[Any, ...]" has no attribute "closed"  [union-attr]
dapr_agents/types/llm.py:617: error: Item "tuple[Any, ...]" of "BufferedReader | IO[Any] | tuple[Any, ...]" has no attribute "closed"  [union-attr]
type: exit 1 (2.27 seconds) /Users/scni/workspace/dapr-agents> mypy --config-file mypy.ini pid=98092
  type: FAIL code 1 (2.29=setup[0.03]+cmd[2.27] seconds)
  evaluation failed :( (2.33 seconds)

Now

tox -e type
type: commands[0] quickstarts/05-multi-agent-workflow-dapr-workflows> mypy --config-file mypy.ini
Success: no issues found in 165 source files
  type: OK (2.16=setup[0.03]+cmd[2.13] seconds)
  congratulations :) (2.20 seconds)

Remaining

tox -e type
type: commands[0] quickstarts/05-multi-agent-workflow-dapr-workflows> mypy --config-file mypy.ini
dapr_agents/types/llm.py:428: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "model_fields_set"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "dict[Any, Any]" of "OpenAIChatCompletionParams | HFHubChatCompletionParams | NVIDIAChatCompletionParams | dict[Any, Any] | Any" has no attribute "model"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "AzureOpenAIModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "name"  [union-attr]
dapr_agents/types/llm.py:429: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "name"  [union-attr]
dapr_agents/types/llm.py:430: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "model_fields_set"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "dict[Any, Any]" of "OpenAIChatCompletionParams | HFHubChatCompletionParams | NVIDIAChatCompletionParams | dict[Any, Any] | Any" has no attribute "model"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "OpenAIModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "HFHubModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "NVIDIAModelConfig" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
dapr_agents/types/llm.py:431: error: Item "dict[Any, Any]" of "OpenAIModelConfig | AzureOpenAIModelConfig | HFHubModelConfig | NVIDIAModelConfig | dict[Any, Any] | Any" has no attribute "azure_deployment"  [union-attr]
Found 12 errors in 1 file (checked 165 source files)
dapr_agents/types/llm.py:553: error: Item "tuple[Any, ...]" of "BufferedReader | IO[Any] | tuple[Any, ...]" has no attribute "closed"  [union-attr]
dapr_agents/types/llm.py:617: error: Item "tuple[Any, ...]" of "BufferedReader | IO[Any] | tuple[Any, ...]" has no attribute "closed"  [union-attr]
type: exit 1 (2.27 seconds) /Users/scni/workspace/dapr-agents> mypy --config-file mypy.ini pid=98818
  type: FAIL code 1 (2.30=setup[0.03]+cmd[2.27] seconds)
  evaluation failed :( (2.35 seconds)

@yaron2, @Cyb3rWard0g: How do you best want to deal with the union-attr error where some of the parameters.model will not contain all possible configurations?

Hey @CasperGN! first off, Thank you very much for driving the mypy clean-up!

Knocking the list down from 19 to 12 errors is an awesome first pass. 🚀

Comments

If I understand correctly, all remaining complaints are the union-attr variety and they come from the fact that configuration, parameters, and the file fields are declared as big Union[…, dict[Any, Any], Any]. Because a plain dict (or Any) might flow through, mypy has to assume those objects don’t have .model_fields_set, .name, .azure_deployment, .model, or .closed. right?

How we can resolve them

Option What we change Pros Cons
A – Tighten the type Drop `dict[Any, Any] Any` from the Unions so only the concrete Pydantic classes are legal. • Strongest static safety
• No casts needed
B – Keep dicts, then cast (my vote) After the “if isinstance(configuration, dict): …” coercion, add an explicit cast[…] so mypy knows we’re now holding the concrete subclass. Same for parameters and the file validator that checks .closed. • Users can still hand us raw dicts
• mypy is happy
• A couple of small cast() / assert lines
C – Ignore # type: ignore[union-attr] on the offending lines. • Fastest • Masks real issues; we lose type checking where it matters
from typing import cast

# after coercion
if isinstance(configuration, dict):
    configuration = OpenAIModelConfig(**configuration)

configuration = cast(
    OpenAIModelConfig
    | AzureOpenAIModelConfig
    | HFHubModelConfig
    | NVIDIAModelConfig,
    configuration,
)

Same pattern for parameters, and in the audio validators you can either:

if hasattr(value, "closed") and value.closed:
    ...

or cast the tuple branch to BufferedReader | IO[bytes].


Next steps?

  • Let me know which option you prefer.
    I’m leaning toward B because it keeps the public API unchanged yet gives mypy the precision it needs.
  • I’m happy to push a follow-up commit (or review one from you) that adds the casts and eliminates the final 12 errors so tox -e type is 🟢 across the board.

Thanks again for jumping on this! almost there!

@CasperGN
Copy link
Contributor Author

CasperGN commented Apr 27, 2025

If I understand correctly, all remaining complaints are the union-attr variety and they come from the fact that configuration, parameters, and the file fields are declared as big Union[…, dict[Any, Any], Any]. Because a plain dict (or Any) might flow through, mypy has to assume those objects don’t have .model_fields_set, .name, .azure_deployment, .model, or .closed. right?

Yes this is my understanding as well.


@Cyb3rWard0g I concur with your preference of B.

I don't think C is a good option and it's what is more or less already being done my just excluding errors from mypy.

When looking over the other modules there's a lot of Union[…, dict[Any, Any], Any] setups and thus a lot of union-attr type checking errors so it might be good to get a general decision on 'how-to' for when contributing and then adding this decision in a coding guideline or something.


I'm still a little bit in doubt about how we deal with the 4 remaining

Edit: Figured it out.

mypy is now 🟢 across the board for dapr_agents.types! 🚀

Signed-off-by: Casper Guldbech Nielsen <[email protected]>
Signed-off-by: Casper Guldbech Nielsen <[email protected]>
Signed-off-by: Casper Guldbech Nielsen <[email protected]>
@CasperGN CasperGN requested a review from Cyb3rWard0g April 27, 2025 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants