Skip to content

Commit 990948d

Browse files
Merge branch 'main' into feature/dapr-ext-grpc-aio
2 parents 9411ad6 + 41f44c5 commit 990948d

28 files changed

+8456
-95
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ tox -e type
124124
tox -e examples
125125
```
126126

127+
[Dapr Mechanical Markdown](https://github.com/dapr/mechanical-markdown) is used to test the examples.
128+
129+
If you need to run the examples against a pre-released version of the runtime, you can use the following command:
130+
- Get your daprd runtime binary from [here](https://github.com/dapr/dapr/releases) for your platform.
131+
- Copy the binary to your dapr home folder at $HOME/.dapr/bin/daprd.
132+
Or using dapr cli directly: `dapr init --runtime-version <release version>`
133+
- Now you can run the example with `tox -e examples`.
134+
135+
127136
## Documentation
128137

129138
Documentation is generated using Sphinx. Extensions used are mainly Napoleon (To process the Google Comment Style) and Autodocs (For automatically generating documentation). The `.rst` files are generated using Sphinx-Apidocs.

dapr/aio/clients/grpc/client.py

Lines changed: 112 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
to_bytes,
6464
validateNotNone,
6565
validateNotBlankString,
66+
convert_dict_to_grpc_dict_of_any,
67+
convert_value_to_struct,
6668
)
6769
from dapr.aio.clients.grpc._request import (
6870
EncryptRequestIterator,
@@ -76,13 +78,12 @@
7678
InvokeMethodRequest,
7779
BindingRequest,
7880
TransactionalStateOperation,
79-
ConversationInput,
8081
)
82+
from dapr.clients.grpc import conversation
83+
8184
from dapr.clients.grpc._jobs import Job
8285
from dapr.clients.grpc._response import (
8386
BindingResponse,
84-
ConversationResponse,
85-
ConversationResult,
8687
DaprResponse,
8788
GetSecretResponse,
8889
GetBulkSecretResponse,
@@ -1722,21 +1723,21 @@ async def purge_workflow(self, instance_id: str, workflow_component: str) -> Dap
17221723
async def converse_alpha1(
17231724
self,
17241725
name: str,
1725-
inputs: List[ConversationInput],
1726+
inputs: List[conversation.ConversationInput],
17261727
*,
17271728
context_id: Optional[str] = None,
17281729
parameters: Optional[Dict[str, GrpcAny]] = None,
17291730
metadata: Optional[Dict[str, str]] = None,
17301731
scrub_pii: Optional[bool] = None,
17311732
temperature: Optional[float] = None,
1732-
) -> ConversationResponse:
1733+
) -> conversation.ConversationResponseAlpha1:
17331734
"""Invoke an LLM using the conversation API (Alpha).
17341735
17351736
Args:
17361737
name: Name of the LLM component to invoke
17371738
inputs: List of conversation inputs
17381739
context_id: Optional ID for continuing an existing chat
1739-
parameters: Optional custom parameters for the request
1740+
parameters: Optional custom parameters for the request (raw Python values or GrpcAny objects)
17401741
metadata: Optional metadata for the component
17411742
scrub_pii: Optional flag to scrub PII from inputs and outputs
17421743
temperature: Optional temperature setting for the LLM to optimize for creativity or predictability
@@ -1752,11 +1753,14 @@ async def converse_alpha1(
17521753
for inp in inputs
17531754
]
17541755

1756+
# Convert raw Python parameters to GrpcAny objects
1757+
converted_parameters = convert_dict_to_grpc_dict_of_any(parameters)
1758+
17551759
request = api_v1.ConversationRequest(
17561760
name=name,
17571761
inputs=inputs_pb,
17581762
contextID=context_id,
1759-
parameters=parameters or {},
1763+
parameters=converted_parameters,
17601764
metadata=metadata or {},
17611765
scrubPII=scrub_pii,
17621766
temperature=temperature,
@@ -1766,11 +1770,110 @@ async def converse_alpha1(
17661770
response = await self._stub.ConverseAlpha1(request)
17671771

17681772
outputs = [
1769-
ConversationResult(result=output.result, parameters=output.parameters)
1773+
conversation.ConversationResultAlpha1(
1774+
result=output.result, parameters=output.parameters
1775+
)
17701776
for output in response.outputs
17711777
]
17721778

1773-
return ConversationResponse(context_id=response.contextID, outputs=outputs)
1779+
return conversation.ConversationResponseAlpha1(
1780+
context_id=response.contextID, outputs=outputs
1781+
)
1782+
1783+
except grpc.aio.AioRpcError as err:
1784+
raise DaprGrpcError(err) from err
1785+
1786+
async def converse_alpha2(
1787+
self,
1788+
name: str,
1789+
inputs: List[conversation.ConversationInputAlpha2],
1790+
*,
1791+
context_id: Optional[str] = None,
1792+
parameters: Optional[Dict[str, Union[GrpcAny, Any]]] = None,
1793+
metadata: Optional[Dict[str, str]] = None,
1794+
scrub_pii: Optional[bool] = None,
1795+
temperature: Optional[float] = None,
1796+
tools: Optional[List[conversation.ConversationTools]] = None,
1797+
tool_choice: Optional[str] = None,
1798+
) -> conversation.ConversationResponseAlpha2:
1799+
"""Invoke an LLM using the conversation API (Alpha2) with tool calling support.
1800+
1801+
Args:
1802+
name: Name of the LLM component to invoke
1803+
inputs: List of Alpha2 conversation inputs with sophisticated message types
1804+
context_id: Optional ID for continuing an existing chat
1805+
parameters: Optional custom parameters for the request (raw Python values or GrpcAny objects)
1806+
metadata: Optional metadata for the component
1807+
scrub_pii: Optional flag to scrub PII from inputs and outputs
1808+
temperature: Optional temperature setting for the LLM to optimize for creativity or predictability
1809+
tools: Optional list of tools available for the LLM to call
1810+
tool_choice: Optional control over which tools can be called ('none', 'auto', 'required', or specific tool name)
1811+
1812+
Returns:
1813+
ConversationResponseAlpha2 containing the conversation results with choices and tool calls
1814+
1815+
Raises:
1816+
DaprGrpcError: If the Dapr runtime returns an error
1817+
"""
1818+
1819+
# Convert inputs to proto format
1820+
inputs_pb = []
1821+
for inp in inputs:
1822+
proto_input = api_v1.ConversationInputAlpha2()
1823+
if inp.scrub_pii is not None:
1824+
proto_input.scrub_pii = inp.scrub_pii
1825+
1826+
for message in inp.messages:
1827+
proto_input.messages.append(message.to_proto())
1828+
1829+
inputs_pb.append(proto_input)
1830+
1831+
# Convert tools to proto format
1832+
tools_pb = []
1833+
if tools:
1834+
for tool in tools:
1835+
proto_tool = api_v1.ConversationTools()
1836+
if tool.function:
1837+
proto_tool.function.name = tool.function.name
1838+
if tool.function.description:
1839+
proto_tool.function.description = tool.function.description
1840+
if tool.function.parameters:
1841+
proto_tool.function.parameters.CopyFrom(
1842+
convert_value_to_struct(tool.function.parameters)
1843+
)
1844+
tools_pb.append(proto_tool)
1845+
1846+
# Convert raw Python parameters to GrpcAny objects
1847+
converted_parameters = convert_dict_to_grpc_dict_of_any(parameters)
1848+
1849+
# Build the request
1850+
request = api_v1.ConversationRequestAlpha2(
1851+
name=name,
1852+
inputs=inputs_pb,
1853+
parameters=converted_parameters,
1854+
metadata=metadata or {},
1855+
tools=tools_pb,
1856+
)
1857+
1858+
if context_id is not None:
1859+
request.context_id = context_id
1860+
if scrub_pii is not None:
1861+
request.scrub_pii = scrub_pii
1862+
if temperature is not None:
1863+
request.temperature = temperature
1864+
if tool_choice is not None:
1865+
request.tool_choice = tool_choice
1866+
1867+
try:
1868+
response, call = await self.retry_policy.run_rpc_async(
1869+
self._stub.ConverseAlpha2, request
1870+
)
1871+
1872+
outputs = conversation._get_outputs_from_grpc_response(response)
1873+
1874+
return conversation.ConversationResponseAlpha2(
1875+
context_id=response.context_id, outputs=outputs
1876+
)
17741877

17751878
except grpc.aio.AioRpcError as err:
17761879
raise DaprGrpcError(err) from err

dapr/clients/_constants.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
Copyright 2025 The Dapr Authors
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
"""
17+
Internal constants for the Dapr clients package.
18+
19+
This module contains shared constants that can be imported by various
20+
client modules without creating circular dependencies.
21+
"""
22+
23+
# Encoding and content type constants
24+
DEFAULT_ENCODING = 'utf-8'
25+
DEFAULT_JSON_CONTENT_TYPE = f'application/json; charset={DEFAULT_ENCODING}'

dapr/clients/base.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
from typing import Optional
1818

1919

20-
DEFAULT_ENCODING = 'utf-8'
21-
DEFAULT_JSON_CONTENT_TYPE = f'application/json; charset={DEFAULT_ENCODING}'
22-
23-
2420
class DaprActorClientBase(ABC):
2521
"""A base class that represents Dapr Actor Client."""
2622

0 commit comments

Comments
 (0)