Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
da462ae
application/pdf export to mime map
pocucan-ds Jul 16, 2025
5f6b8a6
impersonation feature for client.
pocucan-ds Jul 16, 2025
38ffe8b
remove impersonation at class level and define it at instance level.
pocucan-ds Jul 18, 2025
466fe37
updated tests
pocucan-ds Jul 18, 2025
5dd7fe4
impersonation same loop
pocucan-ds Jul 18, 2025
238f8fd
updated environment variables.
pocucan-ds Jul 18, 2025
4524989
impersonation support changelog
pocucan-ds Jul 18, 2025
f3e5255
Merge branch 'main' into extension/source/googledrive/impersonator
pocucan-ds Jul 18, 2025
6a65354
updated how to and formatted.
pocucan-ds Jul 18, 2025
4041158
Merge branch 'extension/source/googledrive/impersonator' of https://g…
pocucan-ds Jul 18, 2025
20df7d2
updated for ruff
pocucan-ds Jul 18, 2025
54da82b
updated signature
pocucan-ds Jul 18, 2025
da272a8
Add impersonation attributes to GoogleDriveSource and update tests fo…
maxpill Jul 21, 2025
7f095c1
Merge branch 'main' into feat/gdrive-impersonator
maxpill Jul 21, 2025
55c3f34
Add GoogleDriveExportFormat enum and update MIME type handling in Goo…
maxpill Jul 24, 2025
43e507d
feat: force tool calling support (#751)
GlockPL Aug 6, 2025
5de31a6
Merge branch 'main' into feat/gdrive-impersonator
maxpill Aug 6, 2025
ca72a0f
Merge branch 'feat/gdrive-impersonator' of https://github.com/deepsen…
maxpill Aug 8, 2025
9290b3e
feat: force tool calling support (#751)
GlockPL Aug 6, 2025
c5b3f73
feat: add PydanticAI agents support (#755)
akotyla Aug 6, 2025
4e60aeb
feat: Autogenerate TS types (#727)
jakubduda-dsai Aug 7, 2025
f267d39
fix: prompt consumes same iterator twice (#768)
ds-sebastianchwilczynski Aug 7, 2025
14b1e77
feat(ui): page title & favicon customization (#767)
dazy-ds Aug 7, 2025
d74a13b
feat(google_drive): enhance impersonation capabilities with class-lev…
maxpill Aug 11, 2025
d2d5537
Merge branch 'main' into feat/gdrive-impersonator
maxpill Aug 11, 2025
ac54d78
fix(google_drive): remove redundant comment in credentials file path
maxpill Aug 11, 2025
84e4f7f
Merge branch 'feat/gdrive-impersonator' of https://github.com/deepsen…
maxpill Aug 11, 2025
6309afc
feat(google_drive): add class method for default impersonation target
maxpill Aug 11, 2025
a7352a9
Merge branch 'develop' of https://github.com/deepsense-ai/ragbits int…
maxpill Aug 12, 2025
04ad536
feat: Optional parallel batches execution in ragbits.evaluate.Evaluat…
vladimir-kivi-ds Aug 14, 2025
6cfb38c
feat(auth): Backend authentication into the chat (#761)
GlockPL Aug 14, 2025
67fbd71
feat(lazy-loading): Decreasing the time needed to start the app (#753)
GlockPL Aug 18, 2025
f7c920a
feat(ui): initial auth plugin (#763)
dazy-ds Aug 19, 2025
6316196
Merge branch 'develop' into feat/gdrive-impersonator
GlockPL Aug 19, 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
1 change: 1 addition & 0 deletions .github/workflows/shared-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ jobs:
env:
GOOGLE_DRIVE_CLIENTID_JSON: ${{ secrets.GOOGLE_DRIVE_CLIENTID_JSON }}
GOOGLE_SOURCE_UNIT_TEST_FOLDER: ${{ secrets.GOOGLE_SOURCE_UNIT_TEST_FOLDER }}
GOOGLE_DRIVE_TARGET_EMAIL: ${{ secrets.GOOGLE_DRIVE_TARGET_EMAIL }}

- name: Test Report
uses: mikepenz/action-junit-report@v4
Expand Down
8 changes: 8 additions & 0 deletions docs/how-to/agents/define_and_use_agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ The result is an [AgentResult][ragbits.agents.AgentResult], which includes the m

You can find the complete code example in the Ragbits repository [here](https://github.com/deepsense-ai/ragbits/blob/main/examples/agents/tool_use.py).

## Tool choice
To control what tool is used at first call you could use `tool_choice` parameter. There are the following options:
- "auto": let model decide if tool call is needed
- "none": do not call tool
- "required: enforce tool usage (model decides which one)
- Callable: one of provided tools


## Conversation history
[`Agent`][ragbits.agents.Agent]s can retain conversation context across multiple interactions by enabling the `keep_history` flag when initializing the agent. This is useful when you want the agent to understand follow-up questions without needing the user to repeat earlier details.

Expand Down
37 changes: 37 additions & 0 deletions docs/how-to/sources/google-drive.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,43 @@ async def process_drive_documents():
asyncio.run(process_drive_documents())
```

## Impersonating Google Accounts

You can configure your Google service account to impersonate other users in your Google Workspace domain. This is useful when you need to access files or perform actions on behalf of specific users.

### Step 1: Enable Domain-Wide Delegation

1. **Sign in to the [Google Admin Console](https://admin.google.com/) as a Super Admin.**
2. Navigate to:
**Security > Access and data control > API controls > MANAGE DOMAIN WIDE DELEGATION**
3. Add a new API client or edit an existing one, and include the following OAuth scopes:
- `https://www.googleapis.com/auth/cloud-platform`
- `https://www.googleapis.com/auth/drive`
4. Click **Authorize** or **Save** to apply the changes.

### Step 2: Impersonate a User in Your Code

After configuring domain-wide delegation, you can specify a target user to impersonate when using the `GoogleDriveSource` in your code.

```python
from ragbits.core.sources.google_drive import GoogleDriveSource

target_email = "[email protected]"
credentials_file = "service-account-key.json"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd put link to the part about creating this credential_file here.

This how-to is very long and I believe someone may forgot what this credential file is all about long ago. Or if they come directly to this place, they're cooked


# Set the path to your service account key file
GoogleDriveSource.set_credentials_file_path(credentials_file)

# Set the email address of the user to impersonate
GoogleDriveSource.set_impersonation_target(target_email)
```

**Note:**
- The `target_email` must be a valid user in your Google Workspace domain.
- Ensure your service account has been granted domain-wide delegation as described above.

This setup allows your service account to act on behalf of the specified user, enabling access to their Google Drive files and resources as permitted by the assigned scopes.

## Troubleshooting

### Common Issues
Expand Down
2 changes: 1 addition & 1 deletion examples/agents/tool_use.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async def main() -> None:
tools=[get_weather],
default_options=AgentOptions(max_total_tokens=500, max_turns=5),
)
response = await agent.run(WeatherPromptInput(location="Paris"))
response = await agent.run(WeatherPromptInput(location="Paris"), tool_choice=get_weather)
print(response)


Expand Down
2 changes: 1 addition & 1 deletion packages/ragbits-agents/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# CHANGELOG

## Unreleased
- Add tool_choice parameter to agent interface (#738)

## 1.2.1 (2025-08-04)

Expand All @@ -13,7 +14,6 @@
### Changed

- ragbits-core updated to version v1.2.0

- Add native openai tools support (#621)
- add Context to Agents (#715)

Expand Down
23 changes: 21 additions & 2 deletions packages/ragbits-agents/src/ragbits/agents/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
)
from ragbits.agents.mcp.server import MCPServer
from ragbits.agents.mcp.utils import get_tools
from ragbits.agents.tool import Tool, ToolCallResult
from ragbits.agents.tool import Tool, ToolCallResult, ToolChoice
from ragbits.core.audit.traces import trace
from ragbits.core.llms.base import LLM, LLMClientOptionsT, LLMResponseWithMetadata, ToolCall, Usage
from ragbits.core.options import Options
Expand Down Expand Up @@ -192,6 +192,7 @@ async def run(
input: str | None = None,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AgentResult[PromptOutputT]: ...

@overload
Expand All @@ -200,13 +201,15 @@ async def run(
input: PromptInputT,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AgentResult[PromptOutputT]: ...

async def run(
self,
input: str | PromptInputT | None = None,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AgentResult[PromptOutputT]:
"""
Run the agent. The method is experimental, inputs and outputs may change in the future.
Expand All @@ -218,6 +221,11 @@ async def run(
- None: No input. Only valid when a string prompt was provided during initialization.
options: The options for the agent run.
context: The context for the agent run.
tool_choice: Parameter that allows to control what tool is used at first call. Can be one of:
- "auto": let model decide if tool call is needed
- "none": do not call tool
- "required: enforce tool usage (model decides which one)
- Callable: one of provided tools

Returns:
The result of the agent run.
Expand Down Expand Up @@ -251,6 +259,7 @@ async def run(
await self.llm.generate_with_metadata(
prompt=prompt_with_history,
tools=[tool.to_function_schema() for tool in tools_mapping.values()],
tool_choice=tool_choice if tool_choice and turn_count == 0 else None,
options=self._get_llm_options(llm_options, merged_options, context.usage),
),
)
Expand Down Expand Up @@ -294,6 +303,7 @@ def run_streaming(
input: str | None = None,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AgentResultStreaming: ...

@overload
Expand All @@ -302,13 +312,15 @@ def run_streaming(
input: PromptInputT,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AgentResultStreaming: ...

def run_streaming(
self,
input: str | PromptInputT | None = None,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AgentResultStreaming:
"""
This method returns an `AgentResultStreaming` object that can be asynchronously
Expand All @@ -318,6 +330,11 @@ def run_streaming(
input: The input for the agent run.
options: The options for the agent run.
context: The context for the agent run.
tool_choice: Parameter that allows to control what tool is used at first call. Can be one of:
- "auto": let model decide if tool call is needed
- "none": do not call tool
- "required: enforce tool usage (model decides which one)
- Callable: one of provided tools

Returns:
A `StreamingResult` object for iteration and collection.
Expand All @@ -329,14 +346,15 @@ def run_streaming(
AgentInvalidPromptInputError: If the prompt/input combination is invalid.
AgentMaxTurnsExceededError: If the maximum number of turns is exceeded.
"""
generator = self._stream_internal(input, options, context)
generator = self._stream_internal(input, options, context, tool_choice)
return AgentResultStreaming(generator)

async def _stream_internal(
self,
input: str | PromptInputT | None = None,
options: AgentOptions[LLMClientOptionsT] | None = None,
context: AgentRunContext | None = None,
tool_choice: ToolChoice | None = None,
) -> AsyncGenerator[str | ToolCall | ToolCallResult | SimpleNamespace | BasePrompt | Usage]:
if context is None:
context = AgentRunContext()
Expand All @@ -357,6 +375,7 @@ async def _stream_internal(
streaming_result = self.llm.generate_streaming(
prompt=prompt_with_history,
tools=[tool.to_function_schema() for tool in tools_mapping.values()],
tool_choice=tool_choice if tool_choice and turn_count == 0 else None,
options=self._get_llm_options(llm_options, merged_options, context.usage),
)
async for chunk in streaming_result:
Expand Down
5 changes: 4 additions & 1 deletion packages/ragbits-agents/src/ragbits/agents/tool.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any
from typing import Any, Literal

from typing_extensions import Self

Expand Down Expand Up @@ -76,3 +76,6 @@ def to_function_schema(self) -> dict[str, Any]:
"parameters": self.parameters,
},
}


ToolChoice = Literal["auto", "none", "required"] | Callable
Loading
Loading