Compare commits

...

243 Commits

Author SHA1 Message Date
amoghmc 71b7429a42
Add missing C# and F# kernels (#6283)
Add missing C# and F# kernels.
These are needed to create a default kernel else it will return an empty
kernel.
2025-04-15 20:15:28 +00:00
cheng-tan 88dda88f53
Pin opentelemetry-proto version (#6305)
## Description
This PR pins opentelemetry-proto version to >=1.28.0, which uses
protobuf > 5.0, < 6.0 to generate protobuf files.

## Related issue number
Closes #6304
2025-04-15 09:04:01 -07:00
Yash Malik 7e8472f99b
minor grammatical fix in docs (#6263)
minor grammatical fix in docs

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-15 06:14:44 +00:00
Abhijeetsingh Meena 756aef366d
Add code generation support to `CodeExecutorAgent` (#6098)
## Why are these changes needed?
- To add support for code generation, execution and reflection to
`CodeExecutorAgent`.

## Related issue number
Closes #5824 

## Checks
- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Signed-off-by: Abhijeetsingh Meena <abhijeet040403@gmail.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-14 23:06:40 -07:00
Sungjun.Kim 71a4eaedf9
Bump up json-schema-to-pydantic from v0.2.3 to v0.2.4 (#6300)
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-14 21:50:49 -07:00
Victor Dibia 95b1ed5d81
Update AutoGen dependency range in AGS (#6298) 2025-04-15 04:26:30 +00:00
Eric Zhu c75515990e
Update website for 0.5.2 (#6299) 2025-04-14 20:51:45 -07:00
Eric Zhu 3500170be1
update version 0.5.2 (#6296)
Update version
2025-04-14 18:03:44 -07:00
larry 3425d7dc2c
Fix typo in multi-agent-debate.ipynb (#6288) 2025-04-13 03:59:37 +00:00
masquerlin eca80ff663
Update discover.md with adding email agent package (#6274)
adding email agent

## Why are these changes needed?

This PR introduces an AI-powered email assistant that can generate
images, attach files, draft reports, and send emails to multiple
recipients or specific users based on their queries. This feature is
highly beneficial for customer management and email marketing, enhancing
automation and improving efficiency.

## Related issue number

Open #6228 
## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.
2025-04-11 09:35:18 -04:00
Ricky Loynd 92df415edf
Expose TCM TypedDict classes for apps to use (#6269)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

An app can pass untyped dicts to set configuration options of various
Task-Centric Memory classes. But tools like pyright can complain about
the loose typing. This PR exposes 4 TypedDict classes that apps can
optionally use.

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-04-10 15:55:21 -07:00
湛露先生 973774b27f
Fix publish_message-method() notes (#6250)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-04-10 18:51:05 +00:00
Shyam Sathish d70cdf8223
Fix ValueError: Dataclass has a union type error (#6266)
Closes #6265

Convert the `Message` and `Resource` dataclasses to Pydantic models in
the `llamaindex-agent` cookbook.

* Replace `dataclass` with `BaseModel` for `Message` and `Resource`
classes.
* Update imports to use `BaseModel` from `pydantic`

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-04-10 11:38:13 -07:00
Macon Pegram 196be34cb6
[Bugfix] Fix for Issue #6241 - ChromaDB removed IncludeEnum (#6260)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

`IncludeEnum` was removed in ChromaDB when it was updated to `1.0.0`.
This caused issues when using `ChromaDBVectorMemory`. This PR fixes
those issues

## Related issue number

Closes #6241

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-04-10 09:41:41 -07:00
Victor Dibia 1226a4f763
Add note on ModelInfo for Gemini Models (#6259)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Add note on how to update modelinfo for new models.

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #6258

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-04-10 08:53:22 -07:00
Xiaoyun Zhang 7abbdc8a6d
.NET update autogen 0.2.2 -> autogen 0.2.3 (#6257)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-04-09 17:42:46 +00:00
Victor Dibia 32d2a18bf1
[Draft] Enable File Upload/Paste as Task in AGS (#6091)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?



https://github.com/user-attachments/assets/e160f16d-f42d-49e2-a6c6-687e4e6786f4



Enable file upload/paste as a task in AGS. Enables tasks like

- Can you research and fact check the ideas in this screenshot?
- Summarize this file

Only text and images supported for now
Underneath, it constructs TextMessage and Multimodal messages as the
task.

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5773 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-04-09 02:44:45 +00:00
Jay Prakash Thakur cc806a57ef
Bugfix/azure ai search embedding (#6248)
## Why are these changes needed?

bug fix : add get_embedding() implementation

## Related issue number

"Closes #6240 " -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [X] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [X] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-09 00:19:18 +00:00
Ricky Loynd b3f59057fa
Expose more Task-Centric Memory parameters (#6246)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

- Exposes a few optional memory controller parameters for more detailed
control and evaluation.
- Fixes a couple formatting issues in the documentation.

## Related issue number

None

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.
2025-04-08 15:13:34 -07:00
Artur ac315ef3ce
fix: typo in usage.md (#6245)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.
2025-04-08 15:05:05 -04:00
Xiaoyun Zhang 2230161447
.NET update oai and aoai package version (#6239)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-04-07 14:51:56 -07:00
Michael Scovetta a024790c5b
Fix sha256_hash docstring (#6236)
Fixes the sha256_hash docstring to refer to SHA-256 and not MD5.

## Why are these changes needed?

The docstring refers to (presumably) a previous implementation that was
using MD5.

## Related issue number

N/A

## Checks

- [X] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [N/A] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [N/A] I've made sure all auto checks have passed.
2025-04-07 15:34:32 -04:00
Eric Zhu f564781fef
Update json_schema_to_pydantic version and make relaxed requirement on arry item. (#6209)
Resolves #6152
2025-04-07 18:44:18 +00:00
湛露先生 8fe627e48c
Fix terminations conditions. (#6229)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
2025-04-07 15:24:54 +00:00
Hussein Mozannar 7acfd8a9d8
Docker Code Exec delete temp files (#6211)
This pull request introduces a new feature to the
`DockerCommandLineCodeExecutor` class, which allows temporary files
generated by code execution to be deleted after code execution. The most
important changes include adding a new configuration option, updating
the class to handle this option, and adding tests to verify the new
functionality.

### New Feature: Temporary File Deletion

*
[`python/packages/autogen-ext/src/autogen_ext/code_executors/docker/_docker_code_executor.py`](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R81):
Added `delete_tmp_files` attribute to the
`DockerCommandLineCodeExecutorConfig` class and updated the
`DockerCommandLineCodeExecutor` class to handle this attribute. This
includes initializing the attribute, adding it to the configuration
methods, and implementing the file deletion logic in the
`_execute_code_dont_check_setup` method.
[[1]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R81)
[[2]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R128)
[[3]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R177)
[[4]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R231)
[[5]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R318)
[[6]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R346-R352)
[[7]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R527)
[[8]](diffhunk://#diff-8ef47c21141ed8b0a757b0e6f9d1491561fc31684756d22ed0253edbfcfcdf91R547)

### Testing

*
[`python/packages/autogen-ext/tests/code_executors/test_docker_commandline_code_executor.py`](diffhunk://#diff-635dbdcdeca161e620283399d5cd43ca756ec0f88d4429f059ee4f6b346874e4R318-R363):
Added a new test `test_delete_tmp_files` to verify the behavior of the
`delete_tmp_files` attribute. This test checks that temporary files are
correctly deleted or retained based on the configuration.<!-- Thank you
for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->
2025-04-06 18:47:35 +00:00
湛露先生 b6705115d1
clean codes notes for autogen-core. (#6218)
Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-05 22:03:07 -07:00
EeS b24df29ad0
Fix/transformer aware any modelfamily (#6213)
This PR improves fallback safety when an invalid `model_family` is
supplied to `get_transformer()`. Previously, if a user passed an
arbitrary or incorrect `family` string in `model_info`, the lookup could
fail without falling back to `ModelFamily.UNKNOWN`.

Now, we explicitly check whether `model_family` is a valid value in
`ModelFamily.ANY`. If not, we fallback to `_find_model_family()` as
intended.


## Related issue number

Related #6011#issuecomment-2779957730

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-05 19:58:16 -07:00
Leonardo Pinheiro faf2a4e6ff
chore: Add powershell path check for code executor (#6212) 2025-04-06 02:41:06 +00:00
Ardent Illumina b1ae4ac79e
added: gemini 2.5 pro preview (#6226) 2025-04-06 00:27:56 +00:00
湛露先生 0a314c17c7
fix autostudio dabase jsons. (#6183)
Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-04-04 15:00:56 -07:00
湛露先生 27e1656e06
Remove redundancy code and improve validation logics in AgentChat (#6190)
Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-04 21:17:43 +00:00
EeS 39321266f9
Improve SocietyOfMindAgent message handling (#6142)
Please refer to #6123  for full context.

That issue outlines several design and behavioral problems with
`SocietyOfMindAgent`.
This DRAFT PR focuses on resolving the most critical and broken
behaviors first.

Here is the error list
🔍 SocietyOfMindAgent: Design Issues and Historical Comparison (v0.2 vs
v0.4+)

###  P1–P4 Regression Issue Table (Updated with Fixes in PR #6142)

| ID | Description | Current v0.4+ Issue | Resolution in PR #6142 | Was
it a problem in v0.2? | Notes |

|-----|-------------|----------------------|--------------------------|----------------------------|-------|
| **P1** | `inner_messages` leaks into outer team termination evaluation
| `Response.inner_messages` is appended to the outer team's
`_message_thread`, affecting termination conditions. Violates
encapsulation. |  `inner_messages` is excluded from `_message_thread`,
avoiding contamination of outer termination logic. |  No | Structural
boundary is now enforced |
| **P2** | Inner team does not execute when outer message history is
empty | In chained executions, if no new outer message exists, no task
is created and the inner team is skipped entirely |  Detects absence of
new outer message and reuses the previous task, passing it via a handoff
message. This ensures the inner team always receives a valid task to
execute |  No | The issue was silent task omission, not summary
failure. Summary succeeds as a downstream effect |
| **P3** | Summary LLM prompt is built from external input only | Prompt
is constructed using external message history, ignoring internal
reasoning |  Prompt construction now uses
`final_response.inner_messages`, restoring internal reasoning as the
source of summarization |  No | Matches v0.2 internal monologue
behavior |
| **P4** | External input is included in summary prompt (possibly
incorrectly) | Outer messages are used in the final LLM summarization
prompt |  Resolved via the same fix as P3; outer messages are no longer
used for summary |  No | Redundant with P3, now fully addressed |


<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number
resolve #6123 
Blocked #6168 (Sometimes SoMA send last whitespace message)
related #6187
<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-04 13:50:50 -07:00
湛露先生 687946258f
Clean chess examples. (#6203)
Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-04 10:57:40 -07:00
EeS f9268204ad
[BUGFIX] Add LLMCallEventMessage to resolve instantiation error in AgentStudio (#6204)
This PR defines the missing `LLMCallEventMessage` class to resolve an
instantiation error that occurs when using custom messages (e.g., via
AgentStudio).

> **Discord Report**  
> சravanaன் — 오후 6:40  
> _“i updated agentchat and agentcore and tried running the config from
agentstudio and it is now not running the agent and is throwing error
`TypeError: Can't instantiate abstract class LLMCallEventMessage with
abstract methods to_model_message, to_model_text, to_text`”_

The issue stems from `LLMCallEventMessage` being an abstract class that
lacks required methods from `BaseChatMessage`.
This PR implements the missing methods.

Since `LLMCallEventMessage` is intended for **logging/UI use only**, and
not to be sent to LLMs, the `to_model_message()` method raises
`NotImplementedError` by design.


## Related issue number

Reported in Discord 
Closes #6206
2025-04-04 10:24:55 -07:00
Eric Zhu b62b12e3e7
Update website verison (#6196) 2025-04-03 15:57:40 -07:00
Eric Zhu 47602eac9e
Update version to 0.5.1 (#6195) 2025-04-03 15:10:41 -07:00
Eric Zhu d4ac2ca6de
Fix streaming + tool bug in Ollama (#6193)
Fix a bug that caused tool calls to be truncated in
OllamaChatCompletionClient when streaming is on.
2025-04-03 14:56:01 -07:00
Eric Zhu 5508cc7a43
Update versions to 0.5.0 (#6184) 2025-04-02 18:15:50 -07:00
Victor Dibia bd572cc112
Ensure message sent to LLMCallEvent for Anthropic is serializable (#6135)
Messages sent as part of `LLMCallEvent` for Anthropic were not fully serializable
The example below shows TextBlock and ToolUseBlocks inside the content of messages - these throw downsteam errors in apps like AGS (or event sinks) that expect serializable dicts inside the LLMCallEvent.
```
[
{'role': 'user', 'content': 'What is the weather in New York?'}, 
{'role': 'assistant', 'content': [TextBlock(citations=None, text='I can help you find the weather in New York. Let me check that for you.', type='text'), ToolUseBlock(id='toolu_016W8g55GejYGBzRRrcsnt7M', input={'city': 'New York'}, name='get_weather', type='tool_use')]}, 
{'role': 'user', 'content': [{'type': 'tool_result', 'tool_use_id': 'toolu_016W8g55GejYGBzRRrcsnt7M', 'content': 'The weather in New York is 73 degrees and Sunny.'}]}
]


```
This PR attempts to first serialize content of anthropic messages before they are passed to `LLMCallEvent`

```
[
{'role': 'user', 'content': 'What is the weather in New York?'}, 
{'role': 'assistant', 'content': [{'citations': None, 'text': 'I can help you find the weather in New York. Let me check that for you.', 'type': 'text'}, {'id': 'toolu_016W8g55GejYGBzRRrcsnt7M', 'input': {'city': 'New York'}, 'name': 'get_weather', 'type': 'tool_use'}]}, 
{'role': 'user', 'content': [{'type': 'tool_result', 'tool_use_id': 'toolu_016W8g55GejYGBzRRrcsnt7M', 'content': 'The weather in New York is 73 degrees and Sunny.'}]}
]

```
2025-04-02 18:01:42 -07:00
Jay Prakash Thakur 0d9b574d09
Add Azure AI Search tool implementation (#5844)
# Azure AI Search Tool Implementation

This PR adds a new tool for Azure AI Search integration to autogen-ext,
enabling agents to search and retrieve information from Azure AI Search
indexes.

## Why Are These Changes Needed?
AutoGen currently lacks native integration with Azure AI Search, which
is a powerful enterprise search service that supports semantic, vector,
and hybrid search capabilities. This integration enables agents to:
1. Retrieve relevant information from large document collections
2. Perform semantic search with AI-powered ranking
3. Execute vector similarity search using embeddings
4. Combine text and vector approaches for optimal results

This tool complements existing retrieval capabilities and provides a
seamless way to integrate with Azure's search infrastructure.

## Features
- **Multiple Search Types**: Support for text, semantic, vector, and
hybrid search
- **Flexible Configuration**: Customizable search parameters and fields
- **Robust Error Handling**: User-friendly error messages with
actionable guidance
- **Performance Optimizations**: Configurable caching and retry
mechanisms
- **Vector Search Support**: Built-in embedding generation with
extensibility

## Usage Example
```python
from autogen_ext.tools.azure import AzureAISearchTool
from azure.core.credentials import AzureKeyCredential
from autogen import AssistantAgent, UserProxyAgent
# Create the search tool
search_tool = AzureAISearchTool.load_component({
   "provider": "autogen_ext.tools.azure.AzureAISearchTool",
   "config": {
       "name": "DocumentSearch",
       "description": "Search for information in the knowledge base",
       "endpoint": "https://your-service.search.windows.net",
       "index_name": "your-index",
       "credential": {"api_key": "your-api-key"},
       "query_type": "semantic",
       "semantic_config_name": "default"
   }
})
# Create an agent with the search tool
assistant = AssistantAgent(
   "assistant",
   llm_config={"tools": [search_tool]}
)
# Create a user proxy agent
user_proxy = UserProxyAgent(
   "user_proxy",
   human_input_mode="TERMINATE",
   max_consecutive_auto_reply=10,
   code_execution_config={"work_dir": "coding"}
)
# Start the conversation
user_proxy.initiate_chat(
   assistant,
   message="What information do we have about quantum computing in our knowledge base?"
)
```

## Testing
- Added unit tests for all search types (text, semantic, vector, hybrid)
- Added tests for error handling and cancellation
- All tests pass locally

## Documentation
- Added comprehensive docstrings with examples
- Included warnings about placeholder embedding implementation
- Added links to Azure AI Search documentation

## Related issue number

Closes #5419 

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-02 23:16:48 +00:00
EeS d7f2b56846
FIX:simple fix on tool calling test for anthropic (#6181)
Just simple change.

```python
messages: List[LLMMessage] = [UserMessage(content="Call the pass tool with input 'task'", source="user")]
```
to
```python
messages: List[LLMMessage] = [UserMessage(content="Call the pass tool with input 'task' and talk result", source="user")]
```

And, now.
Anthropic model could pass that test case
`test_model_client_with_function_calling`.
-> Yup. Before, claude could not pass that test case.

With this change, Claude (Anthropic) models are now able to pass the
test case successfully.

Before this fix, Claude failed to interpret the intent correctly. Now,
it can infer both tool usage and follow-up generation.

This change is backward-compatible with other models (e.g., GPT-4) and
improves cross-model consistency for function-calling tests.
2025-04-02 23:10:11 +00:00
EeS 27da37efc0
[Refactor] model family resolution to support non-prefixed names like Mistral (#6158)
This PR improves how model_family is resolved when selecting a
transformer from the registry.
Previously, model families were inferred using a simple prefix-based
match like:
```
if model.startswith(family): ...
```
This works for cleanly prefixed models (e.g., `gpt-4o`, `claude-3`) but
fails for models like `mistral-large-latest`, `codestral-latest`, etc.,
where prefix-based matching is ambiguous or misleading.

To address this:
	•	model_family can now be passed explicitly (e.g., via ModelInfo)
• _find_model_family() is only used as a fallback when the value is
"unknown"
	•	Transformer lookup is now more robust and predictable
• Example integration in to_oai_type() demonstrates this pattern using
self._model_info["family"]

This change is required for safe support of models like Mistral and
other future models that do not follow standard naming conventions.

Linked to discussion in
[#6151](https://github.com/microsoft/autogen/issues/6151)
Related : #6011

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-02 22:08:17 +00:00
Stuart Leeks 9143e58ef1
Add session_id_param to ACADynamicSessionsCodeExecutor (#6171)
The initializer for ACADynamicSessionsCodeExecutor creates a new GUID to
use as the session ID for dynamic sessions.

In some scenarios it is desirable to be able to re-create the agent
group chat from saved state. In this case, the
ACADynamicSessionsCodeExecutor needs to be associated with a previous
instance (so that any execution state is still valid)

This PR adds a new argument to the initializer to allow a session ID to
be passed in (defaulting to the current behaviour of creating a GUID if
absent).

Closes #6119

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-02 21:39:44 +00:00
EeS 9de16d5f70
Fix/anthropic colud not end with trailing whitespace at assistant content (#6168)
## Why are these changes needed?

This PR fixes a `400 - invalid_request_error` that occurs when using
Anthropic models and the **final message is from the assistant and ends
with trailing whitespace**.

Example error:

```
Error code: 400 - {'error': {'code': 'invalid_request_error', 'message': 'messages: final assistant content cannot end with trailing whitespace', ...}}
```

To unblock ongoing internal usage, this patch introduces an **ad-hoc
fix** that strips trailing whitespace if the model is Anthropic and the
last message is from the assistant.

## Related issue number

Ad-hoc fix for issue discussed here:  
https://github.com/microsoft/autogen/issues/6167

Follow-up structural proposal here:  
https://github.com/microsoft/autogen/issues/6167
https://github.com/microsoft/autogen/issues/6167#issuecomment-2768592840
2025-04-02 00:56:08 +00:00
Eric Zhu aec04e76ec
Stop run when an error occured in a group chat (#6141)
Resolves #5851

* Added GroupChatError event type and terminate a run when an error
occurs in either a participant or the group chat manager
* Raise a RuntimeError from the error message within the group chat run
2025-04-01 20:17:50 +00:00
Eric Zhu 86237c9fdf
Add output_format to AssistantAgent for structured output (#6071)
Resolves #5934

This PR adds ability for `AssistantAgent` to generate a
`StructuredMessage[T]` where `T` is the content type in base model.

How to use?

```python
from typing import Literal

from pydantic import BaseModel

from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.ui import Console

# The response format for the agent as a Pydantic base model.
class AgentResponse(BaseModel):
    thoughts: str
    response: Literal["happy", "sad", "neutral"]


# Create an agent that uses the OpenAI GPT-4o model which supports structured output.
model_client = OpenAIChatCompletionClient(model="gpt-4o")
agent = AssistantAgent(
    "assistant",
    model_client=model_client,
    system_message="Categorize the input as happy, sad, or neutral following the JSON format.",
    # Setting the output format to AgentResponse to force the agent to produce a JSON string as response.
    output_content_type=AgentResponse,
)

result = await Console(agent.run_stream(task="I am happy."))

# Check the last message in the result, validate its type, and print the thoughts and response.
assert isinstance(result.messages[-1], StructuredMessage)
assert isinstance(result.messages[-1].content, AgentResponse)
print("Thought: ", result.messages[-1].content.thoughts)
print("Response: ", result.messages[-1].content.response)
await model_client.close()
```

```
---------- user ----------
I am happy.
---------- assistant ----------
{
  "thoughts": "The user explicitly states they are happy.",
  "response": "happy"
}
Thought:  The user explicitly states they are happy.
Response:  happy
```

---------

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-04-01 20:11:01 +00:00
Federico Villa 9915b65929
Changed Code Executors default directory to temporary directory (#6143)
## Why are these changes needed?

Changed default working directory of code executors, from the current
directory `"."` to Python's
[`tempfile`](https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryDirectory).
These changes simplify file cleanup and prevent the model from accessing
code files or other sensitive data that should not be accessible.
These changes simplify file cleanup and prevent the model from accessing
code files or other sensitive data that should not be accessible.

Changes made:
- The default `work_dir` parameter in code executors is changed to
`None`; when invoking the `start()` method, if not `work_dir` was
specified (`None`) a temporary directory is created.
- The `start()` and `stop()` methods of code executors handle the
creation and cleanup of the working directory, for the default temporary
directory.
- For maintaining backward compatibility:
- A `DeprecationWarning` is emitted when the current dir, `"."`, is used
as `work_dir` as it is in the current code executor implementation. The
deprecation warning is tested in `test_deprecated_warning()`.
- For existing implementation that do not call the `start()` method and
do not specify a `work_dir`, the executors will continue using the
current directory `"."` as the working directory, mantaining backward
compatibility.
- Updated test suites:
- Added tests to confirm that by default code executors use a temporary
directory as their working directory: `test_default_work_dir_is_temp()`;
- Implemented test to ensure that a `DeprecationWarning` is raised when
the current directory is used as the default directory:
`test_deprecated_warning()`;
- Added tests to ensure that errors arise when invalid paths (doesn't
exist or user has not the right permissions) are provided:
`test_error_wrong_path()`.

Feel free to suggest any additions or improvements!

## Related issue number

Close #6041 

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-04-01 10:26:05 -07:00
Eric Zhu 68c1879675
Update mcp version to 1.6.0 to avoid bug in closing client. (#6162)
Upgrade `mcp` package version to >=1.6.0 to avoid bugs causing hanging
when using mcp_server_featch.
2025-04-01 14:54:30 +00:00
effedici c620683ba6
fix: the installation instruction had a missing step (#6166)
## Why are these changes needed?

Following the [guide
instructions](https://microsoft.github.io/autogen/stable//user-guide/autogenstudio-user-guide/installation.html#a-install-from-source-manually)
the user will execute the commands in the wrong directory.

## Related issue number

Closes #6165
2025-04-01 07:44:49 +00:00
湛露先生 20753ad38c
Fix docs typos. (#5975)
Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-31 07:22:38 +00:00
湛露先生 770bf2357a
Fix typos and optimize OrTerminationCondition (#5980)
Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-31 00:14:58 -07:00
EeS 61ba153614
Doc/moudulor transform oai (#6149)
This PR adds a module-level docstring to `_message_transform.py`, as
requested in the review for [PR
#6063](https://github.com/microsoft/autogen/pull/6063).

The documentation includes:
- Background and motivation behind the modular transformer design
- Key concepts such as transformer functions, pipelines, and maps
- Examples of how to define, register, and use transformers
- Design principles to guide future contributions and extensions

By embedding this explanation directly into the module, contributors and
maintainers can more easily understand the structure, purpose, and usage
of the transformer pipeline without needing to refer to external
documents.

## Related issue number

Follow-up to [PR #6063](https://github.com/microsoft/autogen/pull/6063)
2025-03-31 06:39:27 +00:00
EeS fbdd89b46b
[BugFix][Refactor] Modular Transformer Pipeline and Fix Gemini/Anthropic Empty Content Handling (#6063)
## Why are these changes needed?
This change addresses a compatibility issue when using Google Gemini
models with AutoGen. Specifically, Gemini returns a 400 INVALID_ARGUMENT
error when receiving a response with an empty "text" parameter.

The root cause is that Gemini does not accept empty string values (e.g.,
"") as valid inputs in the history of the conversation.

To fix this, if the content field is falsy (e.g., None, "", etc.), it is
explicitly replaced with a single whitespace (" "), which prevents the
Gemini model from rejecting the request.

- **Gemini API compatibility:** Gemini models reject empty assistant
messages (e.g., `""`), causing runtime errors. This PR ensures such
messages are safely replaced with whitespace where appropriate.
- **Avoiding regressions:** Applying the empty content workaround **only
to Gemini**, and **only to valid message types**, avoids breaking OpenAI
or other models.
- **Reducing duplication:** Previously, message transformation logic was
scattered and repeated across different message types and models.
Modularizing this pipeline removes that redundancy.
- **Improved maintainability:** With future model variants likely to
introduce more constraints, this modular structure makes it easier to
adapt transformations without writing ad-hoc code each time.
- **Testing for correctness:** The new structure is verified with tests,
ensuring the bug fix is effective and non-intrusive.

## Summary

This PR introduces a **modular transformer pipeline** for message
conversion and **fixes a Gemini-specific bug** related to empty
assistant message content.

### Key Changes

- **[Refactor]** Extracted message transformation logic into a unified
pipeline to:
  - Reduce code duplication
  - Improve maintainability
  - Simplify debugging and extension for future model-specific logic

- **[BugFix]** Gemini models do not accept empty assistant message
content.
- Introduced `_set_empty_to_whitespace` transformer to replace empty
strings with `" "` only where needed
- Applied it **only** to `"text"` and `"thought"` message types, not to
`"tools"` to avoid serialization errors

- **Improved structure for model-specific handling**
- Transformer functions are now grouped and conditionally applied based
on message type and model family
- This design makes it easier to support future models or combinations
(e.g., Gemini + R1)

- **Test coverage added**
- Added dedicated tests to verify that empty assistant content causes
errors for Gemini
  - Ensured the fix resolves the issue without affecting OpenAI models

---

## Motivation

Originally, Gemini-compatible endpoints would fail when receiving
assistant messages with empty content (`""`).
This issue required special handling without introducing brittle, ad-hoc
patches.

In addressing this, I also saw an opportunity to **modularize** the
message transformation logic across models.
This improves clarity, avoids duplication, and simplifies future
adaptations (e.g., different constraints across model families).

---


## 📘 AutoGen Modular Message Transformer: Design & Usage Guide

This document introduces the **new modular transformer system** used in
AutoGen for converting `LLMMessage` instances to SDK-specific message
formats (e.g., OpenAI-style `ChatCompletionMessageParam`).
The design improves **reusability, extensibility**, and
**maintainability** across different model families.

---

### 🚀 Overview

Instead of scattering model-specific message conversion logic across the
codebase, the new design introduces:

- Modular transformer **functions** for each message type
- Per-model **transformer maps** (e.g., for OpenAI-compatible models)
- Optional **conditional transformers** for multimodal/text hybrid
models
- Clear separation between **message adaptation logic** and
**SDK-specific builder** (e.g., `ChatCompletionUserMessageParam`)

---

### 🧱 1. Define Transform Functions

Each transformer function takes:
- `LLMMessage`: a structured AutoGen message
- `context: dict`: metadata passed through the builder pipeline

And returns:
- A dictionary of keyword arguments for the target message constructor
(e.g., `{"content": ..., "name": ..., "role": ...}`)

```python
def _set_thought_as_content_gemini(message: LLMMessage, context: Dict[str, Any]) -> Dict[str, str | None]:
    assert isinstance(message, AssistantMessage)
    return {"content": message.thought or " "}
```

---

### 🪢 2. Compose Transformer Pipelines

Multiple transformer functions are composed into a pipeline using
`build_transformer_func()`:

```python
base_user_transformer_funcs: List[Callable[[LLMMessage, Dict[str, Any]], Dict[str, Any]]] = [
    _assert_valid_name,
    _set_name,
    _set_role("user"),
]

user_transformer = build_transformer_func(
    funcs=base_user_transformer_funcs,
    message_param_func=ChatCompletionUserMessageParam
)
```

- The `message_param_func` is the actual constructor for the target
message class (usually from the SDK).
- The pipeline is **ordered** — each function adds or overrides keys in
the builder kwargs.

---

### 🗂️ 3. Register Transformer Map

Each model family maintains a `TransformerMap`, which maps `LLMMessage`
types to transformers:

```python
__BASE_TRANSFORMER_MAP: TransformerMap = {
    SystemMessage: system_transformer,
    UserMessage: user_transformer,
    AssistantMessage: assistant_transformer,
}

register_transformer("openai", model_name_or_family, __BASE_TRANSFORMER_MAP)
```

- `"openai"` is currently required (as only OpenAI-compatible format is
supported now).
- Registration ensures AutoGen knows how to transform each message type
for that model.

---

### 🔁 4. Conditional Transformers (Optional)

When message construction depends on runtime conditions (e.g., `"text"`
vs. `"multimodal"`), use:

```python
conditional_transformer = build_conditional_transformer_func(
    funcs_map=user_transformer_funcs_claude,
    message_param_func_map=user_transformer_constructors,
    condition_func=user_condition,
)
```

Where:

- `funcs_map`: maps condition label → list of transformer functions
```python
user_transformer_funcs_claude = {
    "text": text_transformers + [_set_empty_to_whitespace],
    "multimodal": multimodal_transformers + [_set_empty_to_whitespace],
}
```

- `message_param_func_map`: maps condition label → message builder
```python
user_transformer_constructors = {
    "text": ChatCompletionUserMessageParam,
    "multimodal": ChatCompletionUserMessageParam,
}
```

- `condition_func`: determines which transformer to apply at runtime
```python
def user_condition(message: LLMMessage, context: Dict[str, Any]) -> str:
    if isinstance(message.content, str):
        return "text"
    return "multimodal"
```

---

### 🧪 Example Flow

```python
llm_message = AssistantMessage(name="a", thought="let’s go")
model_family = "openai"
model_name = "claude-3-opus"

transformer = get_transformer(model_family, model_name, type(llm_message))
sdk_message = transformer(llm_message, context={})
```

---

### 🎯 Design Benefits

| Feature | Benefit |
|--------|---------|
| 🧱 Function-based modular design | Easy to compose and test |
| 🧩 Per-model registry | Clean separation across model families |
| ⚖️ Conditional support | Allows multimodal / dynamic adaptation |
| 🔄 Reuse-friendly | Shared logic (e.g., `_set_name`) is DRY |
| 📦 SDK-specific | Keeps message adaptation aligned to builder interface
|

---

### 🔮 Future Direction

- Support more SDKs and formats by introducing new message_param_func
- Global registry integration (currently `"openai"`-scoped)
- Class-based transformer variant if complexity grows



---

## Related issue number
Closes #5762

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ v ] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-30 21:09:30 -07:00
Eric Zhu 7615c7b83b
Rename to use BaseChatMessage and BaseAgentEvent. Bring back union types. (#6144)
Rename the `ChatMessage` and `AgentEvent` base classes to `BaseChatMessage` and `BaseAgentEvent`. 

Bring back the `ChatMessage` and `AgentEvent` as union of built-in concrete types to avoid breaking existing applications that depends on Pydantic serialization. 

Why?

Many existing code uses containers like this:

```python
class AppMessage(BaseModel):
   name: str
   message: ChatMessage 

# Serialization is this:
m = AppMessage(...)
m.model_dump_json()

# Fields like HandoffMessage.target will be lost because it is now treated as a base class without content or target fields.
```

The assumption on `ChatMessage` or `AgentEvent` to be a union of concrete types could be in many existing code bases. So this PR brings back the union types, while keep method type hints such as those on `on_messages` to use the `BaseChatMessage` and `BaseAgentEvent` base classes for flexibility.
2025-03-30 09:34:40 -07:00
Eric Zhu e686342f53
Fix token limited model context (#6137)
Token limited model context is currently broken because it is importing
from extensions.

This fix removed the imports and updated the model context
implementation to use model client directly.

In the future, the model client's token counting should cache results
from model API to provide accurate counting.
2025-03-28 17:24:41 +00:00
EeS 0cd3ff46fa
FIX: Anthropic and Gemini could take multiple system message (#6118)
Anthropic SDK could not takes multiple system messages.
However some autogen Agent(e.g. SocietyOfMindAgent) makes multiple
system messages.

And... Gemini with OpenaiSDK do not take error. However is not working
mulitple system messages.
(Just last one is working)

So, I simple change of, "merge multiple system message" at these cases.

## Related issue number
Closes #6116
Closes #6117


---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-28 09:05:54 -07:00
Stuart Leeks c24eba6ae1
Add suppress_result_output to ACADynamicSessionsCodeExecutor initializer (#6130)
When using the `ACADynamicSessionsCodeExecutor` it includes the stdout
from the execution but also the `results` property from the call to
dynamic sessions. In some situations, when the executed code results in
a file being saved this is included in the result:

```console
Plot saved as 'results_by_date.png'
{'type': 'image', 'format': 'png', 'base64_data': 'iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOXRFWHRTb2Z0d2FyZQ...
```

In some situations, this additional output is not desirable:
- when displaying the code output to a user - in this case, the stdout
content is dwarfed by the base64 encoded file content
- when an LLM agent is going to evaluate the code output to determine
next steps - in this case, the base64 content will be included in the
message history sent to the LLM increasing the prompt token cost

To handle these cases, this PR adds a new (optional) argument to the
`ACADynamicSessionsCodeExecutor` constructor that would allow
suppressing the result content (but default to False to preserve the
current behaviour in the default case)

(from #6042)
Closes #6042 


Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-28 01:48:18 +00:00
EeS 2754eda611
FEAT: Add missing OpenAI-compatible models (GPT-4.5, Claude models) (#6120)
This PR adds missing model entries for OpenAI-compatible endpoints,
including gpt-4.5-turbo, gpt-4.5-turbo-preview, and claude-3.5-sonnet.
This improves coverage and avoids potential fallback or mismatch issues
when initializing clients.
2025-03-27 18:39:22 -07:00
Griffin Bassman 7487687cdc
[feat] token-limited message context (#6087) 2025-03-27 13:59:27 -07:00
Eric Zhu 29485ef85b
Fix MCP tool bug by dropping unset parameters from input (#6125)
Resolves #6096

Additionally: make sure MCP errors are formatted correctly, added unit
tests for mcp servers and upgrade mcp version.
2025-03-27 13:22:06 -07:00
Jay Prakash Thakur b5ff7ee355
feat(ollama): Add thought field support and fix LLM control parameters (#6126) 2025-03-26 23:14:26 -07:00
Eric Zhu 025490a1bd
Use class hierarchy to organize AgentChat message types and introduce StructuredMessage type (#5998)
This PR refactored `AgentEvent` and `ChatMessage` union types to
abstract base classes. This allows for user-defined message types that
subclass one of the base classes to be used in AgentChat.

To support a unified interface for working with the messages, the base
classes added abstract methods for:
- Convert content to string
- Convert content to a `UserMessage` for model client
- Convert content for rendering in console.
- Dump into a dictionary
- Load and create a new instance from a dictionary

This way, all agents such as `AssistantAgent` and `SocietyOfMindAgent`
can utilize the unified interface to work with any built-in and
user-defined message type.

This PR also introduces a new message type, `StructuredMessage` for
AgentChat (Resolves #5131), which is a generic type that requires a
user-specified content type.

You can create a `StructuredMessage` as follow:

```python

class MessageType(BaseModel):
  data: str
  references: List[str]

message = StructuredMessage[MessageType](content=MessageType(data="data", references=["a", "b"]), source="user")

# message.content is of type `MessageType`. 
```

This PR addresses the receving side of this message type. To produce
this message type from `AssistantAgent`, the work continue in #5934.

Added unit tests to verify this message type works with agents and
teams.
2025-03-26 16:19:52 -07:00
Jack Gerrits 8a5ee3de6a
Add autogen user agent to azure openai requests (#6124) 2025-03-26 16:01:42 -07:00
Liu Jia ce92926e78
add read timeout for create_mcp_server_session (#6080)
Closes #6031 

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-26 17:51:09 +00:00
y26s4824k264 0bec835d59
Emit <think> and </think> around reasoning chunks from model_extras in choices.detla
So the behavior of hosted R1 model is the same as locally hosted R1 model.
Addresses: #5989
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-25 16:17:53 -07:00
Kurok1 2e2a314f7e
Take the output of the tool and use that to create the HandoffMessage (#6073)
Take the output of the tool and use that to create the HandoffMessage.
[discussion is
here](https://github.com/microsoft/autogen/discussions/6067#discussion-8117177)

Supports agents to carry specific instructions when performing handoff
operations

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-25 21:38:07 +00:00
Victor Dibia 9a0588347a
add utf encoding in websurfer read file (#6094)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->



<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Add utf encoding to file reading. 
Without this, a default system encoding will be used. On Windows
machines this can default to any local encoding causing errors.

```python
with open(
            os.path.join(os.path.abspath(os.path.dirname(__file__)), "page_script.js"), "rt", encoding="utf-8"
        ) as fh:
```

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #6093


## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-25 09:01:27 -07:00
Jay Prakash Thakur 7047fb8b8d
Add support for thought field in AzureAIChatCompletionClient (#6062)
added support for the thought process in tool calls for
`OpenAIChatCompletionClient`, allowing additional text produced by a
model alongside tool calls to be preserved in the thought field of
`CreateResult`. This PR extends the same functionality to
`AzureAIChatCompletionClient` for consistency across model clients.

#5650
Co-authored-by: Jay Prakash Thakur <jathakur@microsoft.com>
2025-03-24 17:33:10 -07:00
tongyu 47ffaccba1
AssistantAgent.metadata for user/application identity information associated with the agent. #6048 (#6057)
This PR introduces a metadata field in AssistantAgentConfig, allowing
applications to assign and track identity information for agents.
The metadata field is a Dict[str, str] and is included in the
configuration for proper serialization.


---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-23 14:49:57 -07:00
jspv fc2c9978fd
Add model_context property to AssistantAgent (#6072)
AssistantAgent initiation allows one to pass in a model_context, but
there isn't a "public: way to get the existing model_context created by
default.
2025-03-22 20:21:29 -07:00
Abhijeetsingh Meena e28738ac6f
Add async support for `selector_func` and `candidate_func` in `SelectorGroupChat` (#6068) 2025-03-22 11:32:57 -07:00
EeS bca4d7e82f
FIX: Anthropic multimodal(Image) message for Anthropic >= 0.48 aware (#6054)
## Why are these changes needed?
This PR fixes a `TypeError: Cannot instantiate typing.Union` that occurs
when using the `MultimodalWebSurfer_agent` with Anthropic models. The
error was caused by the incorrect usage of `typing.Union` as a class
constructor instead of a type hint within the `_anthropic_client.py`
file. The code was attempting to instantiate `typing.Union`, which is
not allowed. The fix correctly uses `typing.Union` within type hints,
and uses the correct `Base64ImageSourceParam` type. It also updates the
`pyproject.toml` dependency.

## Related issue number
Closes #6035 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [v] I've made sure all auto checks have passed.

---------

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-22 00:46:55 -07:00
Victor Dibia a2add02b12
[Draft] Add Tracing docs to agentchat (#5995)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->


## Why are these changes needed?

- Adds tracing docs page for AgentChat with Jaeger example 
- [x] Runtime tracing: Example code where tracing is done with the
SingleThreaded Runtime, logging all events
- [x] Custom event tracing: Example code logging messages returned from
`team.run_stream()`
- [ ] LLM span tracing .. depends on
https://github.com/microsoft/autogen/issues/5895
 - [ ] [TBD] Distributed tracing 

See
[tracing.ipynb](bdb6ac5315/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/tracing.ipynb)
here

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

#5992

## Open Questions

@ekzu 
- What is the recommended way to directly log custom events like
LLMCallEvents and ToolCallEvents? LogEventhandlers in user code that
become traced spans?
- Currenltly tool calls and their args are already logged (not sure
where this is done), but LLM call events are not. Should we include
samples on this?

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-22 00:09:19 -07:00
cheng-tan 6f784ac186
[Accessibility] Fix: screen reader does not announce theme change and nested nav label (#6061)
## Why are these changes needed?
fix the accessibility issue that screen reader doesn't announce the
theme when it changes

## Related issue number
#5631 (13) (31) (59)

---------

Co-authored-by: peterychang <49209570+peterychang@users.noreply.github.com>
2025-03-21 18:36:56 -04:00
Eric Zhu 26364e3dfb
doc: Improve documentation around model client and tool and how it works under the hood (#6050)
Address some confusion such as #6036

---------

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-21 15:01:47 -07:00
湛露先生 97ccc582fc
Fix some code for clean autogenstudio (#5981)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-21 14:35:56 -07:00
afourney eaef7bab7c
Allow Docker-out-of-docker in AGBench (#6047)
This PR allows docker-out-of-docker scenarios to be run in agbench
(e.g., agent teams that rely on the DockerCommandLineExecutor)

This is becoming increasingly important for benchmarking and testing,
since the behaviors of running local executors can diverge in important
ways.
2025-03-21 12:55:00 -07:00
cheng-tan ff847cccad
[Accessibility] fix screen reader is not announcing 'Copied' information (#6059)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?
<!-- Please give a short summary of the change and the problem this
solves. -->
If user tab to a code block copy button then hit enter, screen reader
doesn't announce "Copied". This PR fixed this bug.


## Related issue number
#5631 (8)
<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-21 13:17:29 -04:00
Stuart Leeks 9a536e5d2b
Update migration guide type name (#5978)
Update AzureContainerCodeExecutor to ACADynamicSessionsCodeExecutor to
match the type name in the target docs
2025-03-21 02:52:25 +00:00
Luke Singham 3705ecb9d7
Improve grammar of README.md (#5999)
Minor improvements to the grammar of the README. 

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-21 02:46:22 +00:00
WuYunlong 334209f825
Correct README command examples for chess game sample. (#6008)
Fix outdated script references from chess_game.py to main.py
2025-03-21 02:30:30 +00:00
peterychang 8f58e4704f
Add alt text for clickable cards on website (#6043)
## Why are these changes needed?

Fixes (53) on screen reader issues. A special thanks to @sjay8 for
starting the work on this task

## Related issue number

https://github.com/microsoft/autogen/issues/5631
2025-03-21 00:29:24 +00:00
peterychang a21a60b4f3
add alt text to images (#6045)
Adds alt text to images. fixes screen reader issue (41)

## Related issue number

https://github.com/microsoft/autogen/issues/5631
2025-03-20 17:23:35 -07:00
gagb 878aa4c3fc
Add linter to AGBench (#6022)
This pull request introduces a new linting feature to the benchmark
configuration in the `agbench` package. The main changes include adding
a new command to the CLI, implementing the linter functionality, and
integrating it with the existing codebase.

### New Linting Feature:

*
[`python/packages/agbench/src/agbench/cli.py`](diffhunk://#diff-0eafed70ad5e99e6f7319927bf92ee3ce4787d156dd2775b10a61baad7ec1799R10):
Added `lint_cli` import and integrated the new "lint" command into the
`main` function.
[[1]](diffhunk://#diff-0eafed70ad5e99e6f7319927bf92ee3ce4787d156dd2775b10a61baad7ec1799R10)
[[2]](diffhunk://#diff-0eafed70ad5e99e6f7319927bf92ee3ce4787d156dd2775b10a61baad7ec1799R37-R41)

### Linter Implementation:

*
[`python/packages/agbench/src/agbench/linter/__init__.py`](diffhunk://#diff-45842e728e3daad063b3cf84d5857a4fdfe14e6d977fb2054f284eb9f5bb5272R1-R4):
Added necessary imports to initialize the linter module.
*
[`python/packages/agbench/src/agbench/linter/_base.py`](diffhunk://#diff-f7ea2f6706232406b6c727fda6d71f09c568b4573f070af79bb7f3da3514e364R1-R81):
Defined core classes such as `Document`, `Code`, `CodeExample`,
`CodedDocument`, and the `BaseQualitativeCoder` protocol.
*
[`python/packages/agbench/src/agbench/linter/cli.py`](diffhunk://#diff-e6ad1e14dc0df2c10fe62fede5a06d83865ad1961f99ec2d78f9052feb4d663bR1-R86):
Implemented the `lint_cli` function, which includes loading log files,
coding them, and printing the results.
*
[`python/packages/agbench/src/agbench/linter/coders/oai_coder.py`](diffhunk://#diff-5059129410822c8a214f797a6167cbfcfbe31bd6a3b1efcb65a2dd703ef9b331R1-R212):
Implemented the `OAIQualitativeCoder` class to interact with OpenAI for
coding documents and caching results.

Example usage:

<img width="997" alt="image"
src="https://github.com/user-attachments/assets/6718688e-9917-4a43-a2f1-1105b030528d"
/>


<img width="999" alt="image"
src="https://github.com/user-attachments/assets/7fcb9c43-70f2-4fe7-ae29-5ad6a4ef2a16"
/>

> If you are in VSCode Terminal, you can click on the links in the
terminal output to jump to the exact error.

---------

Co-authored-by: afourney <adamfo@microsoft.com>
2025-03-20 19:05:42 +00:00
Hussein Mozannar fef953e062
Fix bytes in markdown converter playwright (#6044)
Fix error:

TypeError: Input stream must be opened in bytes mode, not in text mode.

Markdown converter takes binary stream
2025-03-20 11:53:53 -07:00
Eric Zhu 46add11ec7
Move start() and stop() as interface methods for CodeExecutor (#6040)
Resolves #6015
2025-03-20 10:00:52 -07:00
peterychang 989d99dabe
Announce current page on sidebar links, version (#5986)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Fixes Screen Reader issue (58)

## Related issue number

https://github.com/microsoft/autogen/issues/5631

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-03-20 15:49:11 +00:00
afourney ecdb74b1ef
Limit what files and folders FileSurfer can access. (#6024)
Optionally limit what files and folders FileSurfer can access
(constraining it to a subtree of the FS).

This is not a replacement for Docker sandboxing, but can be used in
conjunction with sandboxing to help prevent FileSurfer from accessing
sensitive files.
2025-03-20 08:35:09 -07:00
Federico Villa 262c74fd41
Properly close model clients in documentation and samples (#5898)
Closes #5873
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-20 07:50:14 +00:00
EdwinInnovation 3498c3ccda
Fix issue #5946: changed code for ACASessionsExecutor _ensure_access_token to be https:/ /dynamicsessions.io/.default (#6001)
## Why are these changes needed?

when I want to create a ACASessionsExecutor instance and execute some
code, the default library imported does not work. It always returns:
"ClientAuthenticationError: Authentication failed: AADSTS70011: The
provided request must include a 'scope' input parameter. The provided
value for the input parameter 'scope' is not valid. The scope
https://dynamicsessions.io/ is not valid. Trace ID:
d75efa58-8be7-44ef-8839-aacfdc850600 Correlation ID:
a8e4d859-92da-4fbe-a8e0-05116323ab55 Timestamp: 2025-03-14 14:15:09Z"

After changing the scope in _ensure_access_token to be
"https://dynamicsessions.io/.default" rather than
""https://dynamicsessions.io/" and it worked.

## Related issue number

 issue #5946

## Checks

- [Y ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ Y] I've made sure all auto checks have passed.

Co-authored-by: edwinwu <edwin@Edwin-MBA.local>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-20 07:26:14 +00:00
Eric Zhu 9103359ef4
add cancellation support to docker executor (#6027)
Resolves #6013
2025-03-19 21:29:01 -07:00
Eric Zhu 855bcd711c
Add API doc for save_state and load_state for SingleThreadedAgentRuntime (#5984)
Resolves #4108
2025-03-19 21:07:30 +00:00
湛露先生 8f42e5a27f
redundancy package delete. (#5976)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

Signed-off-by: zhanluxianshen <zhanluxianshen@163.com>
Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-03-19 20:46:51 +00:00
Eric Zhu 69292e6ff4
Update mimum openai version to 1.66.5 as import path changed (#5996)
Resolves #5994

Open AI moved `openai.types.beta.vector_store` to
`openai.types.vector_store`.
https://github.com/openai/openai-python/compare/v1.65.5...v1.66.0

Also fixed unit tests and use parameterized fixture to run all
scenarios.
2025-03-19 05:20:04 +00:00
Zachary Huang d83927e22a
fix AssistantAgent polymorphism bug (#5967)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Resolve #5953 

## Related issue number
#5953 

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

I have run all [common
tasks](https://github.com/microsoft/autogen/blob/main/python/README.md#common-tasks),
got below errors which I think it is due to no OpenAI API Key is in my
environment variables. Can we ignore them or do I need to buy one?
```
=================================================== short test summary info =================================================== 
ERROR tests/test_db_manager.py::TestDatabaseOperations::test_basic_entity_creation - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
ERROR tests/test_db_manager.py::TestDatabaseOperations::test_upsert_operations - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
ERROR tests/test_db_manager.py::TestDatabaseOperations::test_delete_operations - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
ERROR tests/test_team_manager.py::TestTeamManager::test_load_from_file - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
ERROR tests/test_team_manager.py::TestTeamManager::test_load_from_directory - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
ERROR tests/test_team_manager.py::TestTeamManager::test_create_team - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
ERROR tests/test_team_manager.py::TestTeamManager::test_run_stream - openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_...
=========================================== 3 passed, 5 warnings, 7 errors in 9.07s ===========================================
```

Co-authored-by: Leonardo Pinheiro <leosantospinheiro@gmail.com>
2025-03-19 14:38:36 +10:00
Jacob Alber a2ab0e36d6
ci: Remove --locked from uv sync in Integration test project (#5993)
For some reason --locked is causing the CI to fall over in the codesign
pipeline.

Unclear why it is not happening on the GitHub Actions side - though it
may be an artifact of the way we have the uv cache set up (and the uv
install version does not match between the two). Getting to a building
state, then will investigate further.
2025-03-18 16:34:44 -04:00
Eric Zhu a8cef327f1
Support json schema for response format type in OpenAIChatCompletionClient (#5988)
Resolves #5982

This PR adds support for `json_schema` as a `response_format` type in
`OpenAIChatCompletionClient`. This is necessary because it allows the
client to be serialized along with the schema. If user use
`response_format=SomeBaseModel`, the client cannot be serialized.

Usage:

```python
# Structured output response, with a pre-defined JSON schema.

OpenAIChatCompletionClient(...,
response_format = {
    "type": "json_schema",
    "json_schema": {
        "name": "name of the schema, must be an identifier.",
        "description": "description for the model.",
        # You can convert a Pydantic (v2) model to JSON schema
        # using the `model_json_schema()` method.
        "schema": "<the JSON schema itself>",
        # Whether to enable strict schema adherence when
        # generating the output. If set to true, the model will
        # always follow the exact schema defined in the
        # `schema` field. Only a subset of JSON Schema is
        # supported when `strict` is `true`.
        # To learn more, read
        # https://platform.openai.com/docs/guides/structured-outputs.
        "strict": False,  # or True
    },
},
)
````
2025-03-18 03:14:42 +00:00
Federico Villa 09d8d344a2
Filter invalid parameters in Ollama client requests (#5983)
Remove unrecognized parameters in Ollama API calls.
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-17 21:09:26 +00:00
Abhijeetsingh Meena c4e07e86d8
Implement 'candidate_func' parameter to filter down the pool of candidates for selection (#5954)
## Summary of Changes
- Added 'candidate_func' to 'SelectorGroupChat' to narrow-down the pool
of candidate speakers.
- Introduced a test in tests/test_group_chat_endpoint.py to validate its
functionality.
- Updated the selector group chat user guide with an example
demonstrating 'candidate_func'.

## Why are these changes needed?
- These changes adds a new parameter `candidate_func` to
`SelectorGroupChat` that helps user narrow-down the set of agents for
speaker selection, allowing users to automatically select next speaker
from a smaller pool of agents.

## Related issue number
Closes #5828

## Checks
- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Signed-off-by: Abhijeetsingh Meena <abhijeet040403@gmail.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-17 21:03:25 +00:00
Victor Dibia 8f8ee0478a
AGS - Test Model Component in UI, Compare Sessions (#5963)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

- Adds ability to test model clients in UI after configrations, before
they are used in agents or teams
- Adds UI side by side comparison of sessions
<img width="1878" alt="image"
src="https://github.com/user-attachments/assets/f792d8d6-3f5d-4d8c-a365-5a9e9c00f49e"
/>
<img width="1877" alt="image"
src="https://github.com/user-attachments/assets/5a115a5a-95e1-4956-a733-5f0065711fe3"
/>


<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #4273 
Closes #5728

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-17 11:00:46 -07:00
ZakWork 685142cf51
Fix R1 reasoning parser for openai client (#5961)
R1 reasoning tokens from hosted R1 model were not parsed correctly for the openai client

Resolves #5941

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-17 10:09:41 -07:00
afourney e5ab7d55cf
Some pandas series were not being handled correctly (#5972) 2025-03-17 07:16:18 +00:00
afourney 22b68b96b6
Added a flag to agbench to enable Azure identity. (#5977) 2025-03-17 00:10:44 -07:00
Eric Zhu 483532180a
Improvements to agbench (#5776)
1. Add host network support in Docker and remove unused requirements
from argument check.
2. Use Pandas to simplify summary statistic calculations. 
3. Add running time to summary statistics

```
Using tabulation method defined in '/home/ekzhu/autogen/python/packages/agbench/benchmarks/HumanEval/Scripts/custom_tabulate.py'

    Task Id       Trial 0 Success      Trial 0 Time
--  ------------  -----------------  --------------
 0  HumanEval_0   True                            3
 1  HumanEval_1   False                          15
 2  HumanEval_2   True                            2
 3  HumanEval_3   True                           11
 4  HumanEval_4   True                            4
 5  HumanEval_5   True                            2
 6  HumanEval_6   False                          18
 7  HumanEval_7   True                            2
 8  HumanEval_8   True                            2
 9  HumanEval_9   True                           12
10  HumanEval_10  False                          11
11  HumanEval_11  True                            2
12  HumanEval_12  True                            3
13  HumanEval_13  True                            1
14  HumanEval_14  True                            4
15  HumanEval_15  True                            1
16  HumanEval_16  True                            2
17  HumanEval_17  False                          76
18  HumanEval_18  True                            4
19  HumanEval_19  True                            3
20  HumanEval_20  True                            5
21  HumanEval_21  True                            3
22  HumanEval_22  True                            1
23  HumanEval_23  True                            2
24  HumanEval_24                                nan

Summary Statistics

           Successes    Failures    Missing    Total    Average Success Rate    Average Time    Total Time
-------  -----------  ----------  ---------  -------  ----------------------  --------------  ------------
Trial 0           20           4          1       25                     0.8           7.875           189

CAUTION: 'autogenbench tabulate' is in early preview and is not thoroughly tested.
Please do not cite values from these calculations in academic work without first inspecting and verifying the results in the run logs yourself.

```

Now the default tabulate output looks like this

---------

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-16 09:13:12 -07:00
Eric Zhu aba41d74d3
feat: add structured output to model clients (#5936) 2025-03-15 07:58:13 -07:00
Eric Zhu 9bde5ef911
Improve docs for model clients (#5952)
Address questions related to logging of model client calls and reduce
redundant docs.
2025-03-15 02:28:15 +00:00
Victor Dibia fe1feb3906
Enable Auth in AGS (#5928)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?


https://github.com/user-attachments/assets/b649053b-c377-40c7-aa51-ee64af766fc2

<img width="100%" alt="image"
src="https://github.com/user-attachments/assets/03ba1df5-c9a2-4734-b6a2-0eb97ec0b0e0"
/>


## Authentication

This PR implements an experimental authentication feature to enable
personalized experiences (multiple users). Currently, only GitHub
authentication is supported. You can extend the base authentication
class to add support for other authentication methods.

By default authenticatio is disabled and only enabled when you pass in
the `--auth-config` argument when running the application.

### Enable GitHub Authentication

To enable GitHub authentication, create a `auth.yaml` file in your app
directory:

```yaml
type: github
jwt_secret: "your-secret-key"
token_expiry_minutes: 60
github:
  client_id: "your-github-client-id"
  client_secret: "your-github-client-secret"
  callback_url: "http://localhost:8081/api/auth/callback"
  scopes: ["user:email"]
```

Please see the documentation on [GitHub
OAuth](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app)
for more details on obtaining the `client_id` and `client_secret`.

To pass in this configuration you can use the `--auth-config` argument
when running the application:

```bash
autogenstudio ui --auth-config /path/to/auth.yaml
```

Or set the environment variable:

```bash
export AUTOGENSTUDIO_AUTH_CONFIG="/path/to/auth.yaml"
```

```{note}
- Authentication is currently experimental and may change in future releases
- User data is stored in your configured database
- When enabled, all API endpoints require authentication except for the authentication endpoints
- WebSocket connections require the token to be passed as a query parameter (`?token=your-jwt-token`)

```

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #4350  

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-03-14 15:02:05 -07:00
Eric Zhu 5f9e37dc27
Upgrade llama cpp to 0.3.8 to fix windows related error (#5948)
use the latest version of llama-cpp-python to ensure `uv sync
--all-extras` don't fail on windows.

reference:
https://github.com/microsoft/autogen/pull/5942#issuecomment-2724478534
2025-03-14 12:20:42 -07:00
Nissa Seru 0276aac8fb
Fix `poe check` on Windows (#5942)
`poe check` fails on main on Windows due to a combination line ending
mismatches, Unix-specific commands, and Windows-specific `asyncio`
behavior. This PR attempts to fix this (so that `poe check` on a
freshly-pulled `main` passes on Windows 11.)
2025-03-14 11:44:38 -07:00
Victor Dibia b8b7a2db3a
Ensure SecretStr is cast to str on load for model clients (#5947)
Currently we have SecretStr type for model clients to promote security
best practices.

- when we dump_component, keys are serialized  as SecreteStr ..
- when we load_component ... SecreteStr type is passed to the client in
the api_key field. This i causes the type problems as the clients expect
a string type.

This PR updates the from_config method for model clients to ensure we
get the value from SecretStr.

Closes #5944
2025-03-14 10:15:21 -07:00
Victor Dibia 296de5253a
Update AgentChat Docs for RAGAgent / Teachability (#5935)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?
<img width="1151" alt="image"
src="https://github.com/user-attachments/assets/98bc91ee-749c-4831-b36f-10322979883b"
/>
 
- Update migration guide to cover teachability/rag agents (mention how
similar functionality can be accomplished with AssistantAgent + Memory)
- Update memory docs to explicitly add a text chunking example and a rag
agent

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5772 
Closes #4742

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-13 21:57:47 -07:00
Eric Zhu a4b6372813
Use SecretStr type for api key (#5939)
To prevent accidental export of API keys
2025-03-13 21:29:19 -07:00
afourney 84c622a4cc
Fixes an error that can occur when listing the contents of a directory. (#5938)
Fixes issues like the following trace:

```
packages/autogen_ext/agents/file_surfer/_markdown_file_browser.py", line 39, in __init__
    self.set_path(self._base_path)
  File "/home/hmozannar/webby/.venv/lib/python3.12/site-packages/autogen_ext/agents/file_surfer/_markdown_file_browser.py", line 67, in set_path
    self._open_path(path)
  File "/home/hmozannar/webby/.venv/lib/python3.12/site-packages/autogen_ext/agents/file_surfer/_markdown_file_browser.py", line 210, in _open_path
    io.StringIO(self._fetch_local_dir(path)), file_extension=".txt"
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hmozannar/webby/.venv/lib/python3.12/site-packages/autogen_ext/agents/file_surfer/_markdown_file_browser.py", line 248, in _fetch_local_dir
    mtime = datetime.datetime.fromtimestamp(os.path.getmtime(full_path)).strftime("%Y-%m-%d %H:%M")
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen genericpath>", line 67, in getmtime
PermissionError: [Errno 13] Permission denied: '/home/hmozannar/webby/autogen-studio/frontend/readme.txt'
```
2025-03-13 20:40:30 -07:00
Napat Gun R. fc6fb4ea15
fix: revert to python:3.10-slim base image for AutoGen Studio (#5932)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

This reverts the base image in AutoGen Studio Dockerfile to `FROM
python:3.10-slim`. This fixes the Docker image build failure due to
conflicting UID with Dev Container's `vscode` user.

## Related issue number

Fixes #5929

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-13 16:08:18 -07:00
dependabot[bot] 3b1ed78c64
Bump @babel/helpers from 7.26.9 to 7.26.10 in /python/packages/autogen-studio/frontend (#5905)
Bumps
[@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers)
from 7.26.9 to 7.26.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/babel/babel/releases"><code>@​babel/helpers</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v7.26.10 (2025-03-11)</h2>
<p>Thanks <a
href="https://github.com/jordan-choi"><code>@​jordan-choi</code></a> and
<a
href="https://github.com/mmmsssttt404"><code>@​mmmsssttt404</code></a>
for your first PRs!</p>
<p>This release includes a fix for <a
href="https://github.com/babel/babel/security/advisories/GHSA-968p-4wvh-cqc8">https://github.com/babel/babel/security/advisories/GHSA-968p-4wvh-cqc8</a>,
a security vulnerability which affects the <code>.replace</code> method
of transpiled regular expressions that use named capturing groups.</p>
<h4>👓 Spec Compliance</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17159">#17159</a>
Disallow decorator in array pattern (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-parser</code>, <code>babel-template</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17164">#17164</a>
Fix: always initialize ExportDeclaration attributes (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17142">#17142</a>
fix: &quot;Map maximum size exceeded&quot; in deepClone (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>,
<code>babel-plugin-transform-typescript</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17154">#17154</a>
Update typescript parser tests (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-traverse</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17151">#17151</a>
fix: Should not evaluate vars in child scope (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17153">#17153</a>
fix: Correctly generate <code>abstract override</code> (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17107">#17107</a> Fix
source type detection when parsing TypeScript (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-runtime</code>,
<code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17173">#17173</a> Fix
processing of replacement pattern with named capture groups (<a
href="https://github.com/%5Bmmmsssttt404%5D(https://github.com/mmmsssttt404)"><code>@​mmmsssttt404</code></a>)</li>
</ul>
</li>
</ul>
<h4>💅 Polish</h4>
<ul>
<li><code>babel-standalone</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17158">#17158</a>
Avoid warnings when re-bundling <code>@​babel/standalone</code> with
webpack (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
</ul>
<h4>🏠 Internal</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17160">#17160</a>
Left-value parsing cleanup (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 6</h4>
<ul>
<li>Babel Bot (<a
href="https://github.com/babel-bot"><code>@​babel-bot</code></a>)</li>
<li>Huáng Jùnliàng (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
<li>Nicolò Ribaudo (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
<li>Yunyoung Jordan Choi (<a
href="https://github.com/jordan-choi"><code>@​jordan-choi</code></a>)</li>
<li><a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a></li>
<li><a
href="https://github.com/mmmsssttt404"><code>@​mmmsssttt404</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/babel/babel/blob/main/CHANGELOG.md"><code>@​babel/helpers</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>v7.26.10 (2025-03-11)</h2>
<h4>👓 Spec Compliance</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17159">#17159</a>
Disallow decorator in array pattern (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-parser</code>, <code>babel-template</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17164">#17164</a>
Fix: always initialize ExportDeclaration attributes (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17142">#17142</a>
fix: &quot;Map maximum size exceeded&quot; in deepClone (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>,
<code>babel-plugin-transform-typescript</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17154">#17154</a>
Update typescript parser tests (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-traverse</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17151">#17151</a>
fix: Should not evaluate vars in child scope (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17153">#17153</a>
fix: Correctly generate <code>abstract override</code> (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17107">#17107</a> Fix
source type detection when parsing TypeScript (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-runtime</code>,
<code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17173">#17173</a> Fix
processing of replacement pattern with named capture groups (<a
href="https://github.com/%5Bmmmsssttt404%5D(https://github.com/mmmsssttt404)"><code>@​mmmsssttt404</code></a>)</li>
</ul>
</li>
</ul>
<h4>💅 Polish</h4>
<ul>
<li><code>babel-standalone</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17158">#17158</a>
Avoid warnings when re-bundling <code>@​babel/standalone</code> with
webpack (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
</ul>
<h4>🏠 Internal</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17160">#17160</a>
Left-value parsing cleanup (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e1ce99df42"><code>e1ce99d</code></a>
v7.26.10</li>
<li><a
href="d5952e80c0"><code>d5952e8</code></a>
Fix processing of replacement pattern with named capture groups (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-helpers/issues/17173">#17173</a>)</li>
<li>See full diff in <a
href="https://github.com/babel/babel/commits/v7.26.10/packages/babel-helpers">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@babel/helpers&package-manager=npm_and_yarn&previous-version=7.26.9&new-version=7.26.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/autogen/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-13 22:16:39 +00:00
dependabot[bot] 6bf22ea1df
Bump @babel/runtime from 7.26.9 to 7.26.10 in /python/packages/autogen-studio/frontend (#5904)
Bumps
[@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime)
from 7.26.9 to 7.26.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/babel/babel/releases"><code>@​babel/runtime</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v7.26.10 (2025-03-11)</h2>
<p>Thanks <a
href="https://github.com/jordan-choi"><code>@​jordan-choi</code></a> and
<a
href="https://github.com/mmmsssttt404"><code>@​mmmsssttt404</code></a>
for your first PRs!</p>
<p>This release includes a fix for <a
href="https://github.com/babel/babel/security/advisories/GHSA-968p-4wvh-cqc8">https://github.com/babel/babel/security/advisories/GHSA-968p-4wvh-cqc8</a>,
a security vulnerability which affects the <code>.replace</code> method
of transpiled regular expressions that use named capturing groups.</p>
<h4>👓 Spec Compliance</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17159">#17159</a>
Disallow decorator in array pattern (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-parser</code>, <code>babel-template</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17164">#17164</a>
Fix: always initialize ExportDeclaration attributes (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17142">#17142</a>
fix: &quot;Map maximum size exceeded&quot; in deepClone (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>,
<code>babel-plugin-transform-typescript</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17154">#17154</a>
Update typescript parser tests (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-traverse</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17151">#17151</a>
fix: Should not evaluate vars in child scope (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17153">#17153</a>
fix: Correctly generate <code>abstract override</code> (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17107">#17107</a> Fix
source type detection when parsing TypeScript (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-runtime</code>,
<code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17173">#17173</a> Fix
processing of replacement pattern with named capture groups (<a
href="https://github.com/%5Bmmmsssttt404%5D(https://github.com/mmmsssttt404)"><code>@​mmmsssttt404</code></a>)</li>
</ul>
</li>
</ul>
<h4>💅 Polish</h4>
<ul>
<li><code>babel-standalone</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17158">#17158</a>
Avoid warnings when re-bundling <code>@​babel/standalone</code> with
webpack (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
</ul>
<h4>🏠 Internal</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17160">#17160</a>
Left-value parsing cleanup (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 6</h4>
<ul>
<li>Babel Bot (<a
href="https://github.com/babel-bot"><code>@​babel-bot</code></a>)</li>
<li>Huáng Jùnliàng (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
<li>Nicolò Ribaudo (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
<li>Yunyoung Jordan Choi (<a
href="https://github.com/jordan-choi"><code>@​jordan-choi</code></a>)</li>
<li><a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a></li>
<li><a
href="https://github.com/mmmsssttt404"><code>@​mmmsssttt404</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/babel/babel/blob/main/CHANGELOG.md"><code>@​babel/runtime</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>v7.26.10 (2025-03-11)</h2>
<h4>👓 Spec Compliance</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17159">#17159</a>
Disallow decorator in array pattern (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-parser</code>, <code>babel-template</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17164">#17164</a>
Fix: always initialize ExportDeclaration attributes (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17142">#17142</a>
fix: &quot;Map maximum size exceeded&quot; in deepClone (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>,
<code>babel-plugin-transform-typescript</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17154">#17154</a>
Update typescript parser tests (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-traverse</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17151">#17151</a>
fix: Should not evaluate vars in child scope (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17153">#17153</a>
fix: Correctly generate <code>abstract override</code> (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17107">#17107</a> Fix
source type detection when parsing TypeScript (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-runtime</code>,
<code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17173">#17173</a> Fix
processing of replacement pattern with named capture groups (<a
href="https://github.com/%5Bmmmsssttt404%5D(https://github.com/mmmsssttt404)"><code>@​mmmsssttt404</code></a>)</li>
</ul>
</li>
</ul>
<h4>💅 Polish</h4>
<ul>
<li><code>babel-standalone</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17158">#17158</a>
Avoid warnings when re-bundling <code>@​babel/standalone</code> with
webpack (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
</ul>
<h4>🏠 Internal</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17160">#17160</a>
Left-value parsing cleanup (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e1ce99df42"><code>e1ce99d</code></a>
v7.26.10</li>
<li><a
href="d5952e80c0"><code>d5952e8</code></a>
Fix processing of replacement pattern with named capture groups (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-runtime/issues/17173">#17173</a>)</li>
<li>See full diff in <a
href="https://github.com/babel/babel/commits/v7.26.10/packages/babel-runtime">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@babel/runtime&package-manager=npm_and_yarn&previous-version=7.26.9&new-version=7.26.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/autogen/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-13 22:10:10 +00:00
Victor Dibia 87707bca37
Improve AgentChat Teams Doc (#5930)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

![image](https://github.com/user-attachments/assets/73735a47-f3a6-44f6-8e9d-3a5632c8a80f)


## Why are these changes needed?

The [agentchat teams
docs](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/teams.html)
page did not list out the teams currently supported. This is confusing
for readers/uisers as they have to search around to discover that
selector groupchat, swarm and magentic one are available.

This PR adds a list of supported teams to the top of the teams page and
links to the relevant tutorials.

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-13 21:58:16 +00:00
Yusuf Kaka f1e615321e
Update memory.ipynb - fixed typo chroma_user_memory (#5901)
Fixed a typo, chroma_user_memory instead of user_memory

## Why are these changes needed?

There's a confusing typo in the documentation.

## Related issue number

None

## Checks

- [x ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x ] I've made sure all auto checks have passed.

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-13 21:49:18 +00:00
Eric Zhu 3a1108a575
fix: make sure system message is present in reflection call (#5926)
Resolves #5919
2025-03-13 21:29:46 +00:00
Jacob Alber 41e7e85c40
fix: Improve PublishMessageAsync resilience (#5923)
During the recent fix to SendMessageAsync's recurrence, we added code to ensure blocking on Publish. This adds additional resilience to the Publish delivery, ensuring that every subscribed agent receives the message, regardless of errors in the middle.
2025-03-13 16:53:21 -04:00
Jacob Alber 52346fdb78
ci: Add missing Code Coverage assemblies to M.AG.* tests (#5925)
* Also adds --locked to the `uv sync` call in the Integration Tests project; this will hopefully reduce how much the uv.lock file is mangled during .NET development
2025-03-13 15:56:41 -04:00
Nissa Seru 6ae098fe49
bugfix: Workaround for pydantic/#7713 (#5893)
Use of `SKChatCompletionAdapter` reliably fails with "'MockValSer'
object cannot be converted to 'SchemaSerializer'"; can repro with this
example:
https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/components/model-clients.html#semantic-kernel-adapter

This appears to be related to
https://github.com/pydantic/pydantic/issues/7713 - commit uses
workaround from
https://github.com/pydantic/pydantic/issues/7713#issuecomment-2604574418

## Why are these changes needed?

This unblocks use of the Semantic Kernel integration by addressing the
above-referenced error, enabling the integration to perform as expected.

## Related issue number

N/A, see https://github.com/pydantic/pydantic/issues/7713 for context,
though.

## Checks

- [X] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
 - None needed, internal only change.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- None added; this works on my machine, but I'm not clear on the root
cause of the issue and have no strong opinion on whether this is the
ideal way to fix it long term - simply leaning towards PR`ing a tenative
fix instead of raising an issue.
- [ ] I've made sure all auto checks have passed.
 - I am not familiar with these, but assume they will be run during CI.

---------

Co-authored-by: Leonardo Pinheiro <leosantospinheiro@gmail.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-13 18:23:01 +00:00
Jacob Alber cf1365763c
feat: Implement AgentChat.NET Termination Conditions (#5839)
Closes #5801
2025-03-13 12:41:13 -04:00
afourney aefa66a3ce
Update MarkItDown. (#5920)
Update FileSurfer and WebSurfer to use the latest MarkItDown package.
2025-03-12 21:17:25 -07:00
Eric Zhu 4d8b97eed1
Fix logging error with ollama client (#5917)
Resolves #5910

Co-authored-by: peterychang <49209570+peterychang@users.noreply.github.com>
2025-03-12 16:59:43 -04:00
Griffin Bassman 8199a2c0a1
feat: [dotnet] open control channel for grpc save/load (#5489)
Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-12 15:18:32 -04:00
Jacob Alber 0a988b6393
fix: Recurrent SendMessageAsync deadlock in message handler (#5916)
In the .NET InProcessRuntime we tried to minimize the amount of tasks
created. Unfortunately, this results in a deadlock when a send message
handler is attempting to SendMessage during the handling of the incoming
message.

The fix is to create a new task for delivering the message in the
message processing loop, which creates a new logical stack to run the
delivery, freeing the loop to process the next delivery request.

* Also fixes passing exceptions and cancellations back to the waiting
task.
* Also fixes a build slowdown on Windows due to uv and llama-cpp

Closes #5915
2025-03-12 14:58:07 -04:00
Eric Zhu 712aba6b4e
update ref for v0.4.9 website (#5914)
Needed to fix the homepage display.
2025-03-12 09:00:48 -07:00
peterychang 90332e371b
Revert Allow Voice Access to find clickable cards commit (#5911)
Reverts https://github.com/microsoft/autogen/pull/5857 due to weird
interaction with non-clickable cards
2025-03-12 08:52:13 -07:00
Eric Zhu 2573739111
update website for v0.4.9 (#5906) 2025-03-12 00:05:57 -07:00
Eric Zhu bb8439c7bd
update version to v0.4.9 (#5903) 2025-03-11 19:35:22 -07:00
Eric Zhu 58a5583549
feat: Pause and Resume for AgentChat Teams and Agents (#5887)
1. Add `on_pause` and `on_resume` API to `ChatAgent` to support pausing
behavior when running `on_message` concurrently.
2. Add `GroupChatPause` and `GroupChatResume` RPC events and handle them
in `ChatAgentContainer`.
3. Add `pause` and `resume` API to `BaseGroupChat` to allow for this
behavior accessible from the public API.
4. Improve `SequentialRoutedAgent` class to customize which message
types are sequentially handled, making it possible to have concurrent
handling for some messages (e.g., `GroupChatPause`).
5. Added unit tests. 

See `test_group_chat_pause_resume.py` for how to use this feature. 

What is the difference between pause/resume vs. termination and restart?
- Pause and resume issue direct RPC calls to the participanting agents
of a team while they are running, allowing putting the on-going
generation or actions on hold. This is useful when an agent's turn takes
a long time and multiple steps to complete, and user/application wants
to re-evaluate whether it is worth continue the step or cancel. This
also allows user/application to pause individual agents and resuming
them independently from the team API.
- Termination and restart requires the whole team to comes to a
full-stop, and termination conditions are checked in between agents'
turns. So termination can only happen when no agent is working on its
turn. It is possible that a termination condition has reached well
before the team is terminated, if the agent is taking a long time to
generate a response.

Resolves: #5881
2025-03-11 17:12:34 -07:00
Eitan Yarmush 8fb9ca3a3e
Allow for tracing via context provider (#5889)
These changes allow for 2 important use-cases:
1. Add a span for tool calls which will enable tracing of all tool calls
in agent_chat
2. Allow runtimes to pick up global `tracer_providers` if they are
available. This is very helpful because it allows for nested teams/agent
to all use the same tracer.
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-11 16:33:39 -07:00
Eitan Yarmush 817f728d04
add LLMStreamStartEvent and LLMStreamEndEvent (#5890)
These changes are needed because there is currently no way to get
logging information about Streaming LLM requests/responses.

I decided to put the StreamStart event AFTER the first chunk so there
aren't false positives about connections/auth.

Closes #5730
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-11 15:02:46 -07:00
Victor Dibia 2cc8c73d3b
Fix termination UI in AGS (#5888)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Fix termination UI in AGS, ensure it can be edited correctly

<img width="1269" alt="image"
src="https://github.com/user-attachments/assets/eaa7a92f-a1ea-4ab4-a679-2894ac441311"
/>
<img width="1273" alt="image"
src="https://github.com/user-attachments/assets/6db81068-932f-4d4e-9512-279770c02bf2"
/>
<img width="1270" alt="image"
src="https://github.com/user-attachments/assets/5ca9df7d-b968-46c9-9d62-becd78809273"
/>


<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #5872
## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-11 11:46:25 -07:00
Jacob Alber b4c1e5841f
feat: Save/Load for AgentChat.NET (#5841)
Partially addresses #5800, others pending the Agents PR #5837 and GroupChats PR #5838 coming in to have the classes to attach save/load logic to.
2025-03-11 12:35:03 -04:00
PythicCoder 6a3acc4548
Feature add Add LlamaCppChatCompletionClient and llama-cpp (#5326)
This pull request introduces the integration of the `llama-cpp` library
into the `autogen-ext` package, with significant changes to the project
dependencies and the implementation of a new chat completion client. The
most important changes include updating the project dependencies, adding
a new module for the `LlamaCppChatCompletionClient`, and implementing
the client with various functionalities.

### Project Dependencies:

*
[`python/packages/autogen-ext/pyproject.toml`](diffhunk://#diff-095119d4420ff09059557bd25681211d1772c2be0fbe0ff2d551a3726eff1b4bR34-R38):
Added `llama-cpp-python` as a new dependency under the `llama-cpp`
section.

### New Module:

*
[`python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/__init__.py`](diffhunk://#diff-42ae3ba17d51ca917634c4ea3c5969cf930297c288a783f8d9c126f2accef71dR1-R8):
Introduced the `LlamaCppChatCompletionClient` class and handled import
errors with a descriptive message for missing dependencies.

### Implementation of `LlamaCppChatCompletionClient`:

*
`python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py`:
- Added the `LlamaCppChatCompletionClient` class with methods to
initialize the client, create chat completions, detect and execute
tools, and handle streaming responses.
- Included detailed logging for debugging purposes and implemented
methods to count tokens, track usage, and provide model information.…d
chat capabilities

<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [X ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [X ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ X] I've made sure all auto checks have passed.

---------

Co-authored-by: aribornstein <x@x.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-10 16:53:53 -07:00
Leonardo Pinheiro a1858efac9
feat: update local code executor to support powershell (#5884)
To support powershell on the local code executor.
Closes #5518
2025-03-10 14:00:14 -07:00
Hussein Mozannar 7d17b22925
Add an optional base path to FileSurfer (#5886)
This pull request introduces a new feature to the `FileSurfer` agent and
`MarkdownFileBrowser` by adding support for specifying a base path for
file browsing.

*
`python/packages/autogen-ext/src/autogen_ext/agents/file_surfer/_file_surfer.py`:
* Added `base_path` parameter to `FileSurfer` class and its
initialization method, with a default value of the current working
directory (`os.getcwd()`).
[[1]](diffhunk://#diff-084847b5e64c659c9aff0bd2d05bbcd0fff2c819a4b91bbe65fa0566054c0972R58)
[[2]](diffhunk://#diff-084847b5e64c659c9aff0bd2d05bbcd0fff2c819a4b91bbe65fa0566054c0972R80-R85)
* Updated `MarkdownFileBrowser` initialization within `FileSurfer` to
use the `base_path` parameter.

*
`python/packages/autogen-ext/src/autogen_ext/agents/file_surfer/_markdown_file_browser.py`:
* Added `base_path` parameter to `MarkdownFileBrowser` class and its
initialization method, with a default value of the current working
directory (`os.getcwd()`).
* Updated `MarkdownFileBrowser` to use the `base_path` for setting the
initial path and returning the current page path.
2025-03-09 20:33:18 -07:00
Eric Zhu 99eee0cd27
fix: save_state should not require the team to be stopped. (#5885)
Modify `BaseGroupChat.save_state` to not require the team to be stopped
first. The `save_state` method is read-only. While it may retrieve an
inconsistent state when the team is running, we made a notice to it's
API doc.

Resolves: #5880
2025-03-09 20:09:32 -07:00
Eric Zhu e32f419387
Fix span structure for tracing (#5853)
Resolves #5697
2025-03-08 22:28:11 -08:00
dependabot[bot] dce31cf0d9
Bump axios from 1.7.9 to 1.8.2 in /python/packages/autogen-studio/frontend (#5874)
Bumps [axios](https://github.com/axios/axios) from 1.7.9 to 1.8.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/axios/axios/releases">axios's
releases</a>.</em></p>
<blockquote>
<h2>Release v1.8.2</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>http-adapter:</strong> add allowAbsoluteUrls to path
building (<a
href="https://redirect.github.com/axios/axios/issues/6810">#6810</a>)
(<a
href="fb8eec214c">fb8eec2</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/lexcorp16"
title="+1/-1 ([#6810](https://github.com/axios/axios/issues/6810)
)">Fasoro-Joseph Alexander</a></li>
</ul>
<h2>Release v1.8.1</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>utils:</strong> move <code>generateString</code> to platform
utils to avoid importing crypto module into client builds; (<a
href="https://redirect.github.com/axios/axios/issues/6789">#6789</a>)
(<a
href="36a5a620be">36a5a62</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a
href="https://github.com/DigitalBrainJS" title="+51/-47
([#6789](https://github.com/axios/axios/issues/6789) )">Dmitriy
Mozgovoy</a></li>
</ul>
<h2>Release v1.8.0</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>examples:</strong> application crashed when navigating
examples in browser (<a
href="https://redirect.github.com/axios/axios/issues/5938">#5938</a>)
(<a
href="1260ded634">1260ded</a>)</li>
<li>missing word in SUPPORT_QUESTION.yml (<a
href="https://redirect.github.com/axios/axios/issues/6757">#6757</a>)
(<a
href="1f890b13f2">1f890b1</a>)</li>
<li><strong>utils:</strong> replace getRandomValues with crypto module
(<a
href="https://redirect.github.com/axios/axios/issues/6788">#6788</a>)
(<a
href="23a25af068">23a25af</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>Add config for ignoring absolute URLs (<a
href="https://redirect.github.com/axios/axios/issues/5902">#5902</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/6192">#6192</a>)
(<a
href="32c7bcc0f2">32c7bcc</a>)</li>
</ul>
<h3>Reverts</h3>
<ul>
<li>Revert &quot;chore: expose fromDataToStream to be consumable (<a
href="https://redirect.github.com/axios/axios/issues/6731">#6731</a>)&quot;
(<a
href="https://redirect.github.com/axios/axios/issues/6732">#6732</a>)
(<a
href="1317261125">1317261</a>),
closes <a
href="https://redirect.github.com/axios/axios/issues/6731">#6731</a> <a
href="https://redirect.github.com/axios/axios/issues/6732">#6732</a></li>
</ul>
<h3>BREAKING CHANGES</h3>
<ul>
<li>
<p>code relying on the above will now combine the URLs instead of prefer
request URL</p>
</li>
<li>
<p>feat: add config option for allowing absolute URLs</p>
</li>
<li>
<p>fix: add default value for allowAbsoluteUrls in buildFullPath</p>
</li>
<li>
<p>fix: typo in flow control when setting allowAbsoluteUrls</p>
</li>
</ul>
<h3>Contributors to this release</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/axios/axios/blob/v1.x/CHANGELOG.md">axios's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/axios/axios/compare/v1.8.1...v1.8.2">1.8.2</a>
(2025-03-07)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>http-adapter:</strong> add allowAbsoluteUrls to path
building (<a
href="https://redirect.github.com/axios/axios/issues/6810">#6810</a>)
(<a
href="fb8eec214c">fb8eec2</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/lexcorp16"
title="+1/-1 ([#6810](https://github.com/axios/axios/issues/6810)
)">Fasoro-Joseph Alexander</a></li>
</ul>
<h2><a
href="https://github.com/axios/axios/compare/v1.8.0...v1.8.1">1.8.1</a>
(2025-02-26)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>utils:</strong> move <code>generateString</code> to platform
utils to avoid importing crypto module into client builds; (<a
href="https://redirect.github.com/axios/axios/issues/6789">#6789</a>)
(<a
href="36a5a620be">36a5a62</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a
href="https://github.com/DigitalBrainJS" title="+51/-47
([#6789](https://github.com/axios/axios/issues/6789) )">Dmitriy
Mozgovoy</a></li>
</ul>
<h1><a
href="https://github.com/axios/axios/compare/v1.7.9...v1.8.0">1.8.0</a>
(2025-02-25)</h1>
<h3>Bug Fixes</h3>
<ul>
<li><strong>examples:</strong> application crashed when navigating
examples in browser (<a
href="https://redirect.github.com/axios/axios/issues/5938">#5938</a>)
(<a
href="1260ded634">1260ded</a>)</li>
<li>missing word in SUPPORT_QUESTION.yml (<a
href="https://redirect.github.com/axios/axios/issues/6757">#6757</a>)
(<a
href="1f890b13f2">1f890b1</a>)</li>
<li><strong>utils:</strong> replace getRandomValues with crypto module
(<a
href="https://redirect.github.com/axios/axios/issues/6788">#6788</a>)
(<a
href="23a25af068">23a25af</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>Add config for ignoring absolute URLs (<a
href="https://redirect.github.com/axios/axios/issues/5902">#5902</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/6192">#6192</a>)
(<a
href="32c7bcc0f2">32c7bcc</a>)</li>
</ul>
<h3>Reverts</h3>
<ul>
<li>Revert &quot;chore: expose fromDataToStream to be consumable (<a
href="https://redirect.github.com/axios/axios/issues/6731">#6731</a>)&quot;
(<a
href="https://redirect.github.com/axios/axios/issues/6732">#6732</a>)
(<a
href="1317261125">1317261</a>),
closes <a
href="https://redirect.github.com/axios/axios/issues/6731">#6731</a> <a
href="https://redirect.github.com/axios/axios/issues/6732">#6732</a></li>
</ul>
<h3>BREAKING CHANGES</h3>
<ul>
<li>
<p>code relying on the above will now combine the URLs instead of prefer
request URL</p>
</li>
<li>
<p>feat: add config option for allowing absolute URLs</p>
</li>
<li>
<p>fix: add default value for allowAbsoluteUrls in buildFullPath</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a9f7689b0c"><code>a9f7689</code></a>
chore(release): v1.8.2 (<a
href="https://redirect.github.com/axios/axios/issues/6812">#6812</a>)</li>
<li><a
href="fb8eec214c"><code>fb8eec2</code></a>
fix(http-adapter): add allowAbsoluteUrls to path building (<a
href="https://redirect.github.com/axios/axios/issues/6810">#6810</a>)</li>
<li><a
href="9812045755"><code>9812045</code></a>
chore(sponsor): update sponsor block (<a
href="https://redirect.github.com/axios/axios/issues/6804">#6804</a>)</li>
<li><a
href="72acf75937"><code>72acf75</code></a>
chore(sponsor): update sponsor block (<a
href="https://redirect.github.com/axios/axios/issues/6794">#6794</a>)</li>
<li><a
href="2e64afdff5"><code>2e64afd</code></a>
chore(release): v1.8.1 (<a
href="https://redirect.github.com/axios/axios/issues/6800">#6800</a>)</li>
<li><a
href="36a5a620be"><code>36a5a62</code></a>
fix(utils): move <code>generateString</code> to platform utils to avoid
importing crypto...</li>
<li><a
href="cceb7b1e15"><code>cceb7b1</code></a>
chore(release): v1.8.0 (<a
href="https://redirect.github.com/axios/axios/issues/6795">#6795</a>)</li>
<li><a
href="23a25af068"><code>23a25af</code></a>
fix(utils): replace getRandomValues with crypto module (<a
href="https://redirect.github.com/axios/axios/issues/6788">#6788</a>)</li>
<li><a
href="32c7bcc0f2"><code>32c7bcc</code></a>
feat: Add config for ignoring absolute URLs (<a
href="https://redirect.github.com/axios/axios/issues/5902">#5902</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/6192">#6192</a>)</li>
<li><a
href="4a3e26cf65"><code>4a3e26c</code></a>
chore(config): adjust rollup config to preserve license header to
minified Ja...</li>
<li>Additional commits viewable in <a
href="https://github.com/axios/axios/compare/v1.7.9...v1.8.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=axios&package-manager=npm_and_yarn&previous-version=1.7.9&new-version=1.8.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/autogen/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-08 19:56:16 -08:00
DavidYu00 b69751d88f
Add author name before their message in Chainlit team sample (#5878)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->
This PR makes it clear which agent is speaking per message in the
Chainlit team sample. Previously, messages would be exchanged without
showing who is communicating.

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #5609 

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-08 19:45:49 -08:00
Victor Dibia 134a8c71ef
Add anthropic docs (#5882)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Add anthropic docs

- Add api docs 
- Add sample code + usage in agent chat user guide

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5856 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-08 19:35:28 -08:00
Eric Zhu 704dab1018
Update README to clarify Web Browsing Agent Team usage, and use animated Chromium browser (#5861)
Because it is more fun to see it
2025-03-07 16:47:00 -08:00
Eric Zhu 740afe5b61
Add ToolCallEvent and log it from all builtin tools (#5859)
Resolves #5745

Also made sure to log LLMCallEvent from all builtin model clients, and
added unit test for coverage.

---------

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-07 16:04:45 -08:00
gagb 4ba65601ca
Add new sample: Gitty (#5842) 2025-03-07 23:24:07 +00:00
afourney 8f737de0e1
Add client close (#5871)
Fixes #4821 by adding a `close()` method to all clients.

Additionally:
* The m1 CLI is updated to close the client before exiting.
* The playwrightcontroller is updated to suppress some other unrelated
chatty warnings (e.g,, produced by markitdown when encountering
conversions that require external utilities)
2025-03-07 14:10:06 -08:00
peterychang dd82883a90
Allow Voice Access to find clickable cards (#5857)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Fixes accessibility issue (34)

## Related issue number

#5634 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-07 13:35:15 -05:00
peterychang 97dbc5cd16
word wrap prev/next links on autodocs (#5867)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Long module names get cut off in autodocs. Fixes (3). I would love to
break the words on dots and underscores, but it doesn't look like theres
a CSS option for that

before:

![image](https://github.com/user-attachments/assets/c2e425db-a197-4174-9af8-1f7a805df307)

after:

![image](https://github.com/user-attachments/assets/dcfc261a-61ca-4072-a28f-3b51e428ef7f)


## Related issue number

#5634 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-07 13:22:34 -05:00
afourney 5685bd1888
Update markitdown requirements to >= 0.0.1, while still in the 0.0.x range (#5864) 2025-03-06 21:33:09 -08:00
Eric Zhu ea89a84c30
fix: remove max_tokens from az ai client create call when stream=True (#5860) 2025-03-06 17:18:37 -08:00
Jacob Alber 05b14f197a
feat: Enable Reset in AgentChat.NET (#5855)
Factors out RunContext management into separate classes:
* Defines a LifecycleObject abstraction to manage initialization /
deinitialization
* Defines RunContextStack to enable a stack of init/deinit layers
* Defines a RunManager to own ensuring proper semantics for performing a
"single execution at a time" call while enabling a polymorphic base.

Implements Reset

Closes #5799
Unblocks #5800 (needs RunContext refactor too)
2025-03-06 16:32:29 -08:00
Eric Zhu 907dad46b2
update ollama usage docs (#5854)
Update the outdated ollama usage doc.
2025-03-06 12:53:38 -08:00
peterychang 21770766bf
copy tooltip on focus. Upgrade PDT version (#5848)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

(Partially?) fixes accessibility issue (19). Question out to
accessibility team whether its enough.

Migrating to 16.0 for accessibility fixes. Not moving to 16.1 yet
because of a weird change to the 'Show Source' link's appearance

## Related issue number

#5630 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-06 19:47:36 +00:00
Victor Dibia 648f734c75
Fix component.label error in AGS frontend (#5845)
Fix issue here in this discussion -
https://github.com/microsoft/autogen/discussions/4208#discussioncomment-12394408

<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Fix bug in AGS UI where frontend crashes because the default team config
is null

- update /teams endpoint to always return a default team if none is
found for the user
- update UI to check for team before rendering 
- also update run_id type to be autoincrement int (similar to team id)
instead of uuid. This helps side step the migration failed errors
related to UUID type when using an sqlite backend

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-06 10:52:42 -08:00
Eric Zhu 7e5c1154cf
Support for external agent runtime in AgentChat (#5843)
Resolves #4075

1. Introduce custom runtime parameter for all AgentChat teams
(RoundRobinGroupChat, SelectorGroupChat, etc.). This is done by making
sure each team's topics are isolated from other teams, and decoupling
state from agent identities. Also, I removed the closure agent from the
BaseGroupChat and use the group chat manager agent to relay messages to
the output message queue.
2. Added unit tests to test scenarios with custom runtimes by using
pytest fixture
3. Refactored existing unit tests to use ReplayChatCompletionClient with
a few improvements to the client.
4. Fix a one-liner bug in AssistantAgent that caused deserialized agent
to have handoffs.

How to use it? 

```python
import asyncio
from autogen_core import SingleThreadedAgentRuntime
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination
from autogen_ext.models.replay import ReplayChatCompletionClient

async def main() -> None:
    # Create a runtime
    runtime = SingleThreadedAgentRuntime()
    runtime.start()

    # Create a model client.
    model_client = ReplayChatCompletionClient(
        ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
    )

    # Create agents
    agent1 = AssistantAgent("assistant1", model_client=model_client, system_message="You are a helpful assistant.")
    agent2 = AssistantAgent("assistant2", model_client=model_client, system_message="You are a helpful assistant.")

    # Create a termination condition
    termination_condition = TextMentionTermination("10", sources=["assistant1", "assistant2"])

    # Create a team
    team = RoundRobinGroupChat([agent1, agent2], runtime=runtime, termination_condition=termination_condition)

    # Run the team
    stream = team.run_stream(task="Count to 10.")
    async for message in stream:
        print(message)
    
    # Save the state.
    state = await team.save_state()

    # Load the state to an existing team.
    await team.load_state(state)

    # Run the team again
    model_client.reset()
    stream = team.run_stream(task="Count to 10.")
    async for message in stream:
        print(message)

    # Create a new team, with the same agent names.
    agent3 = AssistantAgent("assistant1", model_client=model_client, system_message="You are a helpful assistant.")
    agent4 = AssistantAgent("assistant2", model_client=model_client, system_message="You are a helpful assistant.")
    new_team = RoundRobinGroupChat([agent3, agent4], runtime=runtime, termination_condition=termination_condition)

    # Load the state to the new team.
    await new_team.load_state(state)

    # Run the new team
    model_client.reset()
    new_stream = new_team.run_stream(task="Count to 10.")
    async for message in new_stream:
        print(message)
    
    # Stop the runtime
    await runtime.stop()

asyncio.run(main())
```

TODOs as future PRs:
1. Documentation.
2. How to handle errors in custom runtime when the agent has exception?

---------

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-03-06 10:32:52 -08:00
Eric Zhu 30b1b8f90c
Fix warning in selector gorup chat guide (#5849)
Fix a warning that has been resolved in the latest release.
2025-03-06 10:24:18 -08:00
Taswar Bhatti 7d17ac8d5b
Update quickstart.ipynb (#5815)
Added notice for user who are not using Jypter Notebook for the example,
to wrap with asyncio
2025-03-06 14:45:27 +00:00
Leonardo Pinheiro 9d235d2585
fix: add plugin to kernel (#5830)
Line that adds the plugin to the kernel was accidentally removed, which
caused SK to be unable to invoke tools.
2025-03-05 04:37:43 +00:00
Eric Zhu 54c309007a
fix: warn when using reflection on tool use with Claude models (#5829)
Resolves #5731
2025-03-05 04:30:19 +00:00
peterychang 92d857f6b4
Keyboard copy event and search bar cancellation (#5820)
## Why are these changes needed?

Keyboard focus location was being lost after a copy event. Header anchor
was also not selectable while hidden

Fixes (2), (4), (11), (35)

## Related issue number

#5630 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-04 18:56:53 +00:00
Ricky Loynd 97536af7a3
Task-Centric Memory (#5227)
_(EXPERIMENTAL, RESEARCH IN PROGRESS)_

In 2023 AutoGen introduced [Teachable
Agents](https://microsoft.github.io/autogen/0.2/blog/2023/10/26/TeachableAgent/)
that users could teach new facts, preferences and skills. But teachable
agents were limited in several ways: They could only be
`ConversableAgent` subclasses, they couldn't learn a new skill unless
the user stated (in a single turn) both the task and how to solve it,
and they couldn't learn on their own. **Task-Centric Memory** overcomes
these limitations, allowing users to teach arbitrary agents (or teams)
more flexibly and reliably, and enabling agents to learn from their own
trial-and-error experiences.

This PR is large and complex. All of the files are new, and most of the
added components depend on the others to run at all. But the review
process can be accelerated if approached in the following order.
1. Start with the [Task-Centric Memory
README](https://github.com/microsoft/autogen/tree/agentic_memory/python/packages/autogen-ext/src/autogen_ext/task_centric_memory).
1. Install the memory extension locally, since it won't be in pypi until
it's merged. In the `agentic_memory` branch, and the `python/packages`
directory:
        - `pip install -e autogen-agentchat`
        - `pip install -e autogen-ext[openai]`
        - `pip install -e autogen-ext[task-centric-memory]`
2. Run the Quickstart sample code, then immediately open the
`./pagelogs/quick/0 Call Tree.html` file in a browser to view the work
in progress.
    3. Click through the web page links to see the details.
2. Continue through the rest of the main README to get a high-level
overview of the architecture.
3. Read through the [code samples
README](https://github.com/microsoft/autogen/tree/agentic_memory/python/samples/task_centric_memory),
running each of the 4 code samples while viewing their page logs.
4. Skim through the 4 code samples, along with their corresponding yaml
config files:
    1. `chat_with_teachable_agent.py`
    2. `eval_retrieval.py`
    3. `eval_teachability.py`
    4. `eval_learning_from_demonstration.py`
    5. `eval_self_teaching.py`
6. Read `task_centric_memory_controller.py`, referring back to the
previously generated page logs as needed. This is the most important and
complex file in the PR.
7. Read the remaining core files.
    1. `_task_centric_memory_bank.py`
    2. `_string_similarity_map.py`
    3. `_prompter.py`
8. Read the supporting files in the utils dir.
    1. `teachability.py`
    2. `apprentice.py`
    3. `grader.py`
    4. `page_logger.py`
    5. `_functions.py`
2025-03-04 09:56:49 -08:00
Eric Zhu 39bfa35e28
docs: Add note recommending PythonCodeExecutionTool as an alternative to CodeExecutorAgent (#5809)
Resolves #5782

---------

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-04 17:48:13 +00:00
peterychang 3855989543
Fix high contrast mode focus (#5796)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Adds sidebar and breadcrumb selection and focus indicators to high
contrast modes.
Fixes (46), (55), (56)

## Related issue number

 #5633 

This change has no affect on normal color modes, but adds selection and
focus indicators to high contrast modes. I'm not sure how to get rid of
the double bars on nested links, but thats a minor issue

before:

![image](https://github.com/user-attachments/assets/62e7ce65-c3f0-4160-8260-b4153a4a2835)

after:

![image](https://github.com/user-attachments/assets/581ed2a2-5f7c-43c8-a675-2f6c57c6a251)
2025-03-04 17:41:25 +00:00
peterychang a701e3b4fa
highlight focused code output boxes in jupyter notebook pages (#5819)
## Why are these changes needed?

Current webpage theme does not highlight code output boxes. Issues (5)
and (29)

## Related issue number

#5630 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-04 09:29:08 -08:00
Eric Zhu ed08676bd7
doc: update termination tutorial to include FunctionCallTermination condition and fix formatting (#5813) 2025-03-03 23:22:19 -08:00
Eric Zhu 83fb29edd4
Update website for v0.4.8 (#5812) 2025-03-03 22:50:56 -08:00
Eric Zhu e7b47700da
doc: update guide for termination condition and tool usage (#5807)
Resolves #5786

Also updated the termination tutorial to include an example of a custom
termination conditon.

Also added to guide about FunctionTool and MCP tools.
2025-03-03 22:26:27 -08:00
Eric Zhu 4858676bdd
Add examples for custom model context in AssistantAgent and ChatCompletionContext (#5810)
Resolves #5777
2025-03-03 22:19:59 -08:00
Eric Zhu cc1b0cdd51
feat: Add FunctionCallTermination condition (#5808)
Add function call termination condition.

Useful as an alternative to TextMentionTermination for models with tool
call capability.
2025-03-04 03:26:47 +00:00
Paul Barbaste da10765474
Update magentic-one.md (#5779)
## Why are these changes needed?

The current installation command fails in certain shells (e.g., `zsh`,
`fish`) because brackets (`[]`) are interpreted as special characters.
Adding quotes ensures compatibility across different environments,
including Linux, macOS, and Windows.

## Related issue number

No related issue, but this fixes an installation issue encountered by
multiple users.

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-04 00:48:08 +00:00
LuSrackhall 897efcca99
Update installation.md (#5784)
## Why are these changes needed?

* `python3` to `python`: Windows uses `python` for Python 3 by default,
not `python3`.
* `bin` to `scripts`: Windows virtual environments use `Scripts` instead
of `bin`.

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-04 00:41:52 +00:00
Muhammad Junaid 0b9a622b56
Fix: Auto-Convert Pydantic and Dataclass Arguments in AutoGen Tool Calls (#5737)
AutoGen was passing raw dictionaries to functions instead of
constructing Pydantic model or dataclass instances. If a tool function’s
parameter was a Pydantic BaseModel or a dataclass, the function would
receive a dict and likely throw an error or behave incorrectly (since it
expected an object of that type).

This PR addresses problem in AutoGen where tool functions expecting
structured inputs (Pydantic models or dataclasses) were receiving raw
dictionaries. It ensures that structured inputs are automatically
validated and instantiated before function calls. Complete details are
in Issue #5736

[Reproducible Example Code - Failing
Case](https://colab.research.google.com/drive/1hgoP-cGdSZ1-OqQLpwYmlmcExgftDqlO?usp=sharing)
 
<!-- Please give a short summary of the change and the problem this
solves. -->
## Changes Made:
- Inspect function signatures for Pydantic BaseModel and dataclass
annotations.
- Convert input dictionaries into properly instantiated objects using
BaseModel.model_validate() for Pydantic models or standard instantiation
for dataclasses.
  - Raise descriptive errors when validation or instantiation fails.
  - Unit tests have been added to cover all scenarios

Now structured inputs are automatically validated and instantiated
before function calls.

- **Updated Conversion Logic:**  
In the `run()` method, we now inspect the function’s signature and
convert input dictionaries to structured objects. For parameters
annotated with a Pydantic model, we use `model_validate()` to create an
instance; for those annotated with a dataclass, we instantiate the
object using the dataclass constructor. For example:

  ```python
  # Get the function signature.
  sig = inspect.signature(self._func)
  raw_kwargs = args.model_dump()
  kwargs = {}

  # Iterate over the parameters expected by the function.
  for name, param in sig.parameters.items():
      if name in raw_kwargs:
          expected_type = param.annotation
          value = raw_kwargs[name]
# If expected type is a subclass of BaseModel, perform conversion.
if inspect.isclass(expected_type) and issubclass(expected_type,
BaseModel):
              try:
                  kwargs[name] = expected_type.model_validate(value)
              except ValidationError as e:
                  raise ValueError(
f"Error validating parameter '{name}' for function
'{self._func.__name__}': {e}"
                  ) from e
          # If it's a dataclass, instantiate it.
          elif is_dataclass(expected_type):
              try:
cls = expected_type if isinstance(expected_type, type) else
type(expected_type)
                  kwargs[name] = cls(**value)
              except Exception as e:
                  raise ValueError(
f"Error instantiating dataclass parameter '{name}' for function
'{self._func.__name__}': {e}"
                  ) from e
          else:
              kwargs[name] = value
  ```

- **Error Handling Improvements:**  
Conversion steps are wrapped in try/except blocks to raise descriptive
errors when instantiation fails, aiding in debugging invalid inputs.

- **Testing:**  
Unit tests have been added to simulate tool calls (e.g., an `add` tool)
to ensure that with input like:
  ```json
  {"input": {"x": 2, "y": 3}}
  ```
The tool function receives an instance of the expected type and returns
the correct result.


## Related issue number
Closes #5736
 
## Checks
- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.
2025-03-03 16:35:27 -08:00
laurentran 3d5e4c8d7b
Update with correct message types (#5789)
Correcting an error: If CodeReviewResult is not approved, the coder
agents sends a CodeReviewTask back to the reviewer agent, not a
CodeWritingTask.

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-03 23:46:53 +00:00
Victor Dibia 1b51e69602
add api docstring to with_requirements (#5746)
Starting out this draft PR to add documentation for the
`with_requirements` decorator in the `autogen-core` package.

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-03-03 23:35:10 +00:00
Eitan Yarmush 9d4236b1ce
TextMessageTerminationCondition for agentchat (#5742)
Closes #5732 
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-03 23:29:25 +00:00
Leonardo Pinheiro 906b09e451
fix: Update SKChatCompletionAdapter message conversion (#5749)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

The PR introduces two changes.

The first change is adding a name attribute to
`FunctionExecutionResult`. The motivation is that semantic kernel
requires it for their function result interface and it seemed like a
easy modification as `FunctionExecutionResult` is always created in the
context of a `FunctionCall` which will contain the name. I'm unsure if
there was a motivation to keep it out but this change makes it easier to
trace which tool the result refers to and also increases api
compatibility with SK.

The second change is an update to how messages are mapped from autogen
to semantic kernel, which includes an update/fix in the processing of
function results.

## Related issue number

<!-- For example: "Closes #1234" -->

Related to #5675 but wont fix the underlying issue of anthropic
requiring tools during AssistantAgent reflection.

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Leonardo Pinheiro <lpinheiro@microsoft.com>
2025-03-03 23:05:54 +00:00
YASAI03 7e01350d46
fix(studio): Fixed an issue where the app would crash if a team was not set when opening a playground page. (#5794)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

(I'm not familiar with English, so I use Goggle translation a lot. So
please forgive me if I say something rude.)
I started autogen-studio and opened the page in the browser, nothing was
rendered.
I checked using the Developer tool, the following error appeared:
```
TypeError: Cannot read properties of null (reading 'label')
at editor.tsx:114:42
```
I check the implementation, it looks like `team.component` is `null`,
which seems to be caused during initialization process when the team is
not registered.
[source](78ff883d24/python/packages/autogen-studio/frontend/src/components/views/playground/manager.tsx (L199))

So I fixed the issue where the gallery wasn't being retrieved which was
causing the issue.

### Reproduce bug

1. clone this repository
2. open devcontainer(`python/packages/autogen-studio`)
3. Running the application
```sh
cd frontend
yarn build
cd -
OPENAI_API_KEY="" autogenstudio ui --port 8081
```
4. Open `localhost:8081` in browser.

## Related issue number

<!-- For example: "Closes #1234" -->

Probably not found. (sorry if I had to raise an issue before PR)

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [-] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-03-03 14:25:40 -08:00
Peter Jausovec a785cd90f9
add stream_options to openai model (#5788)
stream_options are not part of the model classes, so they won't get
serialized when calling dump_component. Adding this to the model allows
us to store the stream options when the component is serialized.
---------

Signed-off-by: Peter Jausovec <peter.jausovec@solo.io>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-03 21:58:05 +00:00
Victor Dibia 679a9357f8
Fix AGS Cascading Delete Issue (#5804)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

> Hey Victor, this is maybe a bug, but when a session is delete, runs
and messages for that session are not deleted, any reason why to keep
them?

@husseinmozannar 

The main fix is to add a pragma that ensures SQL lite enforces foreign
key constraints.
Also needed to update error messages for autoupgrade of databases. Also
adds a test for cascade deletes and for parts of teammanager

With this fix,

- Messages get deleted when the run is deleted 
- Runs get deleted when sessiosn are deleted 
- Sessions get deleted when a team is deleted 

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-03 21:46:57 +00:00
peterychang 8c9961ecba
add options to ollama client (#5805)
Necessary to configure ollama client

## Related issue number

#5597 

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-03 13:24:14 -08:00
Victor Dibia dd1ade816e
Add Git LFS Note to AGS Readme (#5798)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->



## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

- AGS contains image files which are required for local build /dev
- These files are stored using git lfs which needs to be run before the
files are downloaded
- This PR adds a reminder to the AGS readme to run `git lfs checkout`
after installing git lfs to download the image files

## Related issue number

<!-- For example: "Closes #1234" -->

Close #3418

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-03 12:39:25 -08:00
Jay Prakash Thakur 78ff883d24
docs: add note about markdown code block requirement in CodeExecutorA… (#5785)
## Why are these changes needed?

The CodeExecutorAgent documentation needs to be updated to explicitly
mention that it only processes code properly formatted in markdown code
blocks with triple backticks. This change adds a clear note with
examples to help users understand the required format for code
execution.

## Related issue number

Closes #5771


Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-03-02 17:00:08 -08:00
Victor Dibia b8b13935c9
Make FileSurfer and CodeExecAgent Declarative (#5765)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Make FileSurfer and CodeExecAgent Declarative.
These agent presents are used as part of magentic one and having them
declarative is a precursor to their use in AGS.

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #5607

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-01 15:46:30 +00:00
Victor Dibia 78ef148c88
Add ChromaDBVectorMemory in Extensions (#5308)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->
Shows an example of how to use the `Memory` interface to implement a
just-in-time vector memory based on chromadb.

```python
import os
from pathlib import Path

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console
from autogen_core.memory import MemoryContent, MemoryMimeType
from autogen_ext.memory.chromadb import ChromaDBVectorMemory, PersistentChromaDBVectorMemoryConfig
from autogen_ext.models.openai import OpenAIChatCompletionClient

# Initialize ChromaDB memory with custom config
chroma_user_memory = ChromaDBVectorMemory(
    config=PersistentChromaDBVectorMemoryConfig(
        collection_name="preferences",
        persistence_path=os.path.join(str(Path.home()), ".chromadb_autogen"),
        k=2,  # Return top  k results
        score_threshold=0.4,  # Minimum similarity score
    )
)
# a HttpChromaDBVectorMemoryConfig is also supported for connecting to a remote ChromaDB server

# Add user preferences to memory
await chroma_user_memory.add(
    MemoryContent(
        content="The weather should be in metric units",
        mime_type=MemoryMimeType.TEXT,
        metadata={"category": "preferences", "type": "units"},
    )
)

await chroma_user_memory.add(
    MemoryContent(
        content="Meal recipe must be vegan",
        mime_type=MemoryMimeType.TEXT,
        metadata={"category": "preferences", "type": "dietary"},
    )
)


# Create assistant agent with ChromaDB memory
assistant_agent = AssistantAgent(
    name="assistant_agent",
    model_client=OpenAIChatCompletionClient(
        model="gpt-4o",
    ),
    tools=[get_weather],
    memory=[user_memory],
)

stream = assistant_agent.run_stream(task="What is the weather in New York?")
await Console(stream)

await user_memory.close()
```

```txt
 ---------- user ----------
What is the weather in New York?
---------- assistant_agent ----------
[MemoryContent(content='The weather should be in metric units', mime_type='MemoryMimeType.TEXT', metadata={'category': 'preferences', 'mime_type': 'MemoryMimeType.TEXT', 'type': 'units', 'score': 0.4342913043162201, 'id': '8a8d683c-5866-41e1-ac17-08c4fda6da86'}), MemoryContent(content='The weather should be in metric units', mime_type='MemoryMimeType.TEXT', metadata={'category': 'preferences', 'mime_type': 'MemoryMimeType.TEXT', 'type': 'units', 'score': 0.4342913043162201, 'id': 'f27af42c-cb63-46f0-b26b-ffcc09955ca1'})]
---------- assistant_agent ----------
[FunctionCall(id='call_a8U3YEj2dxA065vyzdfXDtNf', arguments='{"city":"New York","units":"metric"}', name='get_weather')]
---------- assistant_agent ----------
[FunctionExecutionResult(content='The weather in New York is 23 °C and Sunny.', call_id='call_a8U3YEj2dxA065vyzdfXDtNf', is_error=False)]
---------- assistant_agent ----------
The weather in New York is 23 °C and Sunny.
```

Note that MemoryContent object in the MemoryQuery events have useful
metadata like the score and id retrieved memories.

## Related issue number

<!-- For example: "Closes #1234" -->


## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-03-01 07:41:01 -08:00
Peter Jausovec 7bbf8e075d
fix incorrect field name from config to component (#5761)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

The Team DB class doesn't have the "config" field anymore. It has
"component"

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

Co-authored-by: Victor Dibia <victordibia@microsoft.com>
2025-02-28 19:23:59 -08:00
Victor Dibia 6625f89c28
Add support for default model client, in AGS updates to settings UI (#5763)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Add support for default model client, in AGS updates to settings UI. 
A default model client will be used for subsequent background tasks
(e.g, automatically naming sessions to meaningful names).

<img width="1441" alt="image"
src="https://github.com/user-attachments/assets/31675ef4-8f80-4a8c-9762-3d6bcbc6d33d"
/>


<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5685

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-28 16:37:51 -08:00
Stuart Leeks 07a455f239
Fix typo (#5754)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->
Typo in example text

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [X] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [X] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [X] I've made sure all auto checks have passed.
2025-02-28 10:33:19 -05:00
Victor Dibia dd0781a76b
Add Serialization Instruction for MemoryContent (#5727)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

- Running a team generates a MemoryQueryEvent has a MemoryContent field
and is part of the model context
- Saving team state (`team.save_state()`) includes serializing model
context
- MemoryContent has a mime_type field, which was not being properly
serialized.
"Object of type MemoryMimeType is not JSON serializable"

This PR? -> add explicit serialization instruction for the mimetype
field.

```python
import asyncio
import logging
import json
import yaml, aiofiles
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.conditions import MaxMessageTermination
from autogen_core.memory import ListMemory, MemoryContent, MemoryMimeType
from autogen_agentchat.ui import Console

logger = logging.getLogger(__name__)

state_path = "team_state.json"

model_client =  OpenAIChatCompletionClient(model="gpt-4o-mini")

max_msg_termination = MaxMessageTermination(max_messages=3)
# Initialize user memory
user_memory = ListMemory()

# Add user preferences to memory
await user_memory.add(MemoryContent(content="The weather should be in metric units", mime_type=MemoryMimeType.TEXT))

# Create the team.
agent = AssistantAgent(
    name="assistant",
    model_client=model_client,
    system_message="You are a helpful assistant.",
    memory = [user_memory],
)
yoda = AssistantAgent(
    name="yoda",
    model_client=model_client,
    system_message="Repeat the same message in the tone of Yoda.",
)

team = RoundRobinGroupChat(
    [agent, yoda],
    termination_condition=max_msg_termination
)

await Console(team.run_stream(task="Hi, How are you ?"))
 
# Save team state to file.
state = await team.save_state()
with open(state_path, "w") as f:
    json.dump(state, f)
# Load team state from file.
with open("team_state.json", "r") as f:
    team_state = json.load(f)
    
await team.load_state(state) 
await Console( team.run_stream(task="What was the last thing that was said in this conversation "))
```

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5688
## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-26 18:40:00 +00:00
Jack Gerrits 6b68719939
Allow background exceptions to be fatal (#5716)
Closes #4904 

Does not change default behavior in core.

In agentchat, this change will mean that exceptions that used to be
ignored and result in bugs like the group chat stopping are now reported
out to the user application.

---------

Co-authored-by: Ben Constable <benconstable@microsoft.com>
Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-02-26 18:34:53 +00:00
peterychang dc55ec964b
Fix visual accessibility issues 6 and 20 (#5725)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Fixes visual accessibility issues `(6) Luminosity contrast ratio of
focus indicator on 'Venv' control is less than 3:1.` and `(20) "Only
color is used to indicate the focus indicator on the 'Venv', and 'conda'
Tab controls."`

## Related issue number

#5633 

before:
<img width="162" alt="image"
src="https://github.com/user-attachments/assets/ac317e82-c82e-4d3b-910b-e9eb5d087340"
/>

![image](https://github.com/user-attachments/assets/2971043a-8b5b-4521-8135-c411c0ecab1f)

after:
<img width="160" alt="image"
src="https://github.com/user-attachments/assets/8053ae77-4d62-491b-9d78-657646ed71a6"
/>

![image](https://github.com/user-attachments/assets/5c3ac2c3-4813-461b-8958-4665860574a0)
2025-02-26 11:59:07 -05:00
rylativity 5615f40a30
5663 ollama client host (#5674)
@ekzhu should likely be assigned as reviewer

## Why are these changes needed?

These changes address the bug reported in #5663. Prevents TypeError from
being thrown at inference time by ollama AsyncClient when `host` (and
other) kwargs are passed to autogen OllamaChatCompletionClient
constructor.

It also adds ollama as a named optional extra so that the ollama
requirements can be installed alongside autogen-ext (e.g. `pip install
autogen-ext[ollama]`

@ekzhu, I will need some help or guidance to ensure that the associated
test (which requires ollama and tiktoken as dependencies of the
OllamaChatCompletionClient) can run successfully in autogen's test
execution environment.

I have also left the "I've made sure all auto checks have passed" check
below unchecked as this PR is coming from my fork. (UPDATE: auto checks
appear to have passed after opening PR, so I have checked box below)

## Related issue number

Intended to close #5663 

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Ryan Stewart <ryanstewart@Ryans-MacBook-Pro.local>
Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
Co-authored-by: peterychang <49209570+peterychang@users.noreply.github.com>
2025-02-26 11:02:48 -05:00
Victor Dibia 05fc763b8a
add anthropic native support (#5695)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

Claude 3.7 just came out. Its a pretty capable model and it would be
great to support it in Autogen.
This will could augment the already excellent support we have for
Anthropic via the SKAdapters in the following ways

- Based on the ChatCompletion API similar to the ollama and openai
client
- Configurable/serializable (can be dumped) .. this means it can be used
easily in AGS.

## What is Supported 

(video below shows the client being used in autogen studio)

https://github.com/user-attachments/assets/8fb7c17c-9f9c-4525-aa9c-f256aad0f40b



- streaming 
- tool callign / function calling 
- drop in integration with assistant agent. 
- multimodal support

```python

from dotenv import load_dotenv
import os 

load_dotenv()

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_ext.models.anthropic import AnthropicChatCompletionClient 
model_client =   AnthropicChatCompletionClient(
        model="claude-3-7-sonnet-20250219" 
    )

async def get_weather(city: str) -> str:
    """Get the weather for a given city."""
    return f"The weather in {city} is 73 degrees and Sunny."

 
agent = AssistantAgent(
    name="weather_agent",
    model_client=model_client,
    tools=[get_weather],
    system_message="You are a helpful assistant.", 
    # model_client_stream=True,   
)

# Run the agent and stream the messages to the console.
async def main() -> None:
    await Console(agent.run_stream(task="What is the weather in New York?"))
await main()
```

result 

```
messages = [
    UserMessage(content="Write a very short story about a dragon.", source="user"),
]

# Create a stream.
stream = model_client.create_stream(messages=messages)

# Iterate over the stream and print the responses.
print("Streamed responses:")
async for response in stream:  # type: ignore
    if isinstance(response, str):
        # A partial response is a string.
        print(response, flush=True, end="")
    else:
        # The last response is a CreateResult object with the complete message.
        print("\n\n------------\n")
        print("The complete response:", flush=True)
        print(response.content, flush=True)
        print("\n\n------------\n")
        print("The token usage was:", flush=True)
        print(response.usage, flush=True)
```

 

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" --> 

Closes #5205 
Closes #5708

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed. 



cc @rohanthacker
2025-02-26 07:27:41 +00:00
Victor Dibia 1f30622adc
update human in the loop docs for agentchat (#5720)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

update human in the loop docs for agentchat  
..

> Outputs in docs are outdated as summary is not printed out by default
when using Console
[#5590](https://github.com/microsoft/autogen/issues/5590)

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #5590

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-25 23:15:33 -08:00
gagb 173e649aa6
Update README.md for improved clarity and formatting (#5714)
This pull request includes updates to the `README.md` file for the
`autogen-studio` package to improve clarity and accuracy. The most
important changes include a minor rewrite of the project structure
section and updates to the installation instructions (better tabbing).
2025-02-25 19:28:05 -08:00
cedricmendelin b37c192424
Dotnet: Add modelServiceId support to SemanticKernelAgent (#5422)
The `SemanticKernelAgent` class has been updated to include an optional
`modelServiceId` parameter, allowing the specification of a service ID
for the model.

## Why are these changes needed?

Currently, `SemanticKernelAgent` uses the parameterless method for
resolving `IChatCompletionSerivce`. This will fail, when multiple models
are registered in the Kernel.

To support different models registered in the Kernel, I adopted the
resolving of the `IChatCompletionSerivce` within the
`SemanticKernelAgent` with an optional parameter. When it is not set, I
resolve the default instance, otherwise, I use the optional parameter as
a servide id for resolving the `IChatCompletionSerivce` service.

## Related issue number



## Checks

- [x] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
Co-authored-by: Xiaoyun Zhang <bigmiao.zhang@gmail.com>
2025-02-25 20:39:45 +00:00
Jack Gerrits 1380412582
Specify specific UV version should be used (#5711)
Closes #5710
2025-02-25 19:47:20 +00:00
peterychang 73f26792ab
Fix accessibility issue 14 for visual accessibility (#5709)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Fixes `(14) Ensures the contrast between foreground and background
colors meets WCAG 2 AA minimum contrast ratio thresholds`

Note: the color values don't output at the exact values on the
stylesheet. For example, the value `#1774E5` evaluates to `#2274E0` by
the Accessibility Insights app.

## Related issue number

https://github.com/microsoft/autogen/issues/5633

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-25 19:19:14 +00:00
Leonardo Pinheiro a02d08a8ef
Refactor AssistantAgent on_message_stream (#5642)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

I'm unsure if everyone will agree, but I started to look into adding new
logic and found that refactoring into smaller functions would make it
more maintainable.

There is no change in functionality, only a breakdown into smaller
methods to make it more modular and improve readability. There is a lot
of logic in the method and this refactor breaks it down into context
management, llm call and result processing.

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

---------

Co-authored-by: Leonardo Pinheiro <lpinheiro@microsoft.com>
2025-02-25 19:11:35 +00:00
pengjunfeng11 a06b6c8b0b
REF: replaced variable name in TextMentionTermination (#5698)
## Why are these changes needed?

For the sake of subsequent people reading the metacode and following
programming specifications, variable names are updated in combination
with usage scenarios. Mainly contains the variable "_self_text" in
TextMentionTermination
2025-02-25 16:00:41 +00:00
Victor Dibia fbe94dd7ed
Add Token Streaming in AGS , Support Env variables (#5659)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

This PR has 3 main improvements. 

- Token streaming 
- Adds support for environment variables in the app settings 
- Updates AGS to persist Gallery entry in db.

## Adds Token Streaming in AGS.  

Agentchat now supports streaming of tokens via
`ModelClientStreamingChunkEvent `. This PR is to track progress on
supporting that in the AutoGen Studio UI.

If `model_client_stream` is enabled in an assitant agent, then token
will be streamed in UI.

```python
streaming_assistant = AssistantAgent(
    name="assistant",
    model_client=model_client,
    system_message="You are a helpful assistant.",
    model_client_stream=True,  # Enable streaming tokens.
)

```

https://github.com/user-attachments/assets/74d43d78-6359-40c3-a78e-c84dcb5e02a1


## Env Variables 
Also adds support for env variables in AGS Settings

You can set env variables that are loaded just before a team is run.
Handy to set variable to be used by tools etc.

<img width="1291" alt="image"
src="https://github.com/user-attachments/assets/437b9d90-ccee-42f7-be5d-94ab191afd67"
/>


> Note: the set variables are available to the server process.

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #5627  
Closes #5662 
Closes #5619 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-25 15:21:08 +00:00
Jack Gerrits ca86906b62
Change base image to one with arm64 support (#5681) 2025-02-25 12:27:02 +00:00
Hussein Mozannar 4dac9c819e
Add metadata field to basemessage (#5372)
Add metadata field to BaseMessage.

Why?
- additional metadata field can track 1) timestamp if needed, 2) flags
about the message. For instance, a use case is a metadata field
{"internal":"yes"} that would hide messages from being displayed in an
application or studio.

As long as an extra field is added to basemessage that is not consumed
by existing agents, I am happy.



Notes:
- We can also only add it to BaseChatMessage, that would be fine
- I don't care what the extra field is called as long as there is an
extra field somewhere
- I don't have preference for the type, a str could work, but a dict
would be more useful.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-02-25 06:54:49 +00:00
Eric Zhu c302b5408a
doc: Update SelectorGroupChat doc on how to use O3-mini model. (#5657)
Resolves #5408

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-25 06:44:23 +00:00
Eric Zhu a14aeab6e4
doc & sample: Update documentation for human-in-the-loop and UserProxyAgent; Add UserProxyAgent to ChainLit sample; (#5656)
Resolves #5610

And address various questions regarding to how to use user proxy agent
and human-in-the-loop.
2025-02-25 01:41:15 +00:00
Jack Gerrits a54a85e2f8
Update issue templates (#5686) 2025-02-25 01:15:50 +00:00
Eric Zhu 6bc896f6e2
update versions to 0.4.8 (#5689) 2025-02-24 23:46:37 +00:00
Ryan Sweet 78adf32f7d
pack agenthost as tool (#5647)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

convenience - allows to just run "agenthost"

```
dotnet pack --no-build --configuration Release --output './output/release' -bl\n
dotnet tool install --add-source ./output/release Microsoft.AutoGen.AgentHost
agenthost 
```

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

closes #5646 

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-24 14:23:45 -08:00
gagb 159e4f114d
Improve readme inconsistency (#5691)
### Before

<img width="823" alt="image"
src="https://github.com/user-attachments/assets/d5ba1671-9433-4fa4-9884-c0de6fafb82e"
/>



### After
<img width="803" alt="image"
src="https://github.com/user-attachments/assets/07fdd32a-d2ad-450d-8b7f-b21f10f14c85"
/>
2025-02-24 12:12:53 -08:00
Shubham Shukla caa363b517
Replace the undefined tools variable with tool_schema parameter in ToolUseAgent class (#5684)
Replace the undefined `tools` variable with `tool_schema` parameter

<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?
This change keeps the documentation up to date :
https://microsoft.github.io/autogen/stable//user-guide/core-user-guide/components/tools.html

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-24 17:41:45 +00:00
Jack Gerrits 181925c95d
[dotnet] Add mixin for easier state save/load apis (#5438)
Co-authored-by: Ryan Sweet <rysweet@microsoft.com>
2025-02-24 16:24:30 +00:00
Ryan Sweet 213da855ec
remove dep on aspire - add google.protobuf (#5645)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

removes unneeded deps

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5644

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-24 16:02:49 +00:00
Eric Zhu 0360ab9715
doc: Enrich AssistantAgent API documentation with usage examples. (#5653)
Resolves #5562
2025-02-24 10:57:34 -05:00
Eric Zhu 08c82e4c6f
docs: Add logging instructions for AgentChat and enhance core logging guide (#5655)
Resolves #5640
2025-02-24 14:39:38 +00:00
Christoph Schittko 64807d24f5
DOCS: Fixed small errors in the text and made code format more consistent (#5664)
## Why are these changes needed?

Fix minor issues in docs:
- probably editing left over
- typo
- code formatting inconsistency

## Related issue number

N/A

## Checks

- [ X] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ X] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ X] I've made sure all auto checks have passed.

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-24 14:28:17 +00:00
Eric Zhu 9fd8eefc55
fix: Structured output with tool calls for OpenAIChatCompletionClient (#5671)
Resolves: #5568

Also, refactored some unit tests.

Integration tests against OpenAI endpoint passed:
https://github.com/microsoft/autogen/actions/runs/13484492096

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-24 14:18:46 +00:00
Ryan Sweet 745c9d2bc5
completes the table on the readme with the .NET links to docs and packages. (#5673)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

completes the table on the readme with the .NET links to docs and
packages.

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-24 14:09:12 +00:00
linznin 2570cc9cf3
Fix: Add support for custom headers in HTTP tool requests (#5660)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

The HttpTool in AutoGen accepts a headers parameter, but it is not being
used in the actual request. This fix ensures that the headers provided
by users are correctly included in HTTP requests. This resolves issues
where authentication or other custom headers are required but currently
ignored.

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->
Closes #5638

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-24 13:52:58 +00:00
Christoph Schittko 87495c67c3
DOCS: Minor updates to handoffs.ipynb (#5665)
Updates:
- added missing backtick for formatting class name
- typo in user response example

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-02-24 06:40:50 +00:00
philippHorn 95585b4408
fix: Crash in argument parsing when using Openrouter (#5667)
## Why are these changes needed?
See issue for a bug description.
The problem was that a lot of openrouter models return `""` as
`tool_call.arguments`, which caused `json.loads` to fail
## Related issue number
https://github.com/microsoft/autogen/issues/5666
---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-02-23 22:35:13 -08:00
Victor Dibia 170b8cc893
Make ChatCompletionCache support component config (#5658)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

This PR makes makes ChatCompletionCache   support component config

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed? 

Ensures we have a path to serializing ChatCompletionCache , similar to
the ChatCompletion client that it wraps.

This PR does the following

- Makes CacheStore serializable first (part of this includes converting
from Protocol to base class). Makes it's derivatives serializable as
well (diskcache, redis)
- Makes ChatCompletionCache serializable 
- Adds some tests

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5141

## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed. 


cc @nour-bouzid
2025-02-23 19:49:22 -08:00
Eric Zhu a226966dbe
fix: Remove R1 model family from is_openai function (#5652) 2025-02-21 16:22:14 -07:00
Eric Zhu 7784f44ea6
feat: Add thought process handling in tool calls and expose ThoughtEvent through stream in AgentChat (#5500)
Resolves #5192

Test

```python
import asyncio
import os
from random import randint
from typing import List
from autogen_core.tools import BaseTool, FunctionTool
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console

async def get_current_time(city: str) -> str:
    return f"The current time in {city} is {randint(0, 23)}:{randint(0, 59)}."

tools: List[BaseTool] = [
    FunctionTool(
        get_current_time,
        name="get_current_time",
        description="Get current time for a city.",
    ),
]

model_client = OpenAIChatCompletionClient(
    model="anthropic/claude-3.5-haiku-20241022",
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
    model_info={
        "family": "claude-3.5-haiku",
        "function_calling": True,
        "vision": False,
        "json_output": False,
    }
)

agent = AssistantAgent(
    name="Agent",
    model_client=model_client,
    tools=tools,
    system_message= "You are an assistant with some tools that can be used to answer some questions",
)

async def main() -> None:
    await Console(agent.run_stream(task="What is current time of Paris and Toronto?"))

asyncio.run(main())
```

```
---------- user ----------
What is current time of Paris and Toronto?
---------- Agent ----------
I'll help you find the current time for Paris and Toronto by using the get_current_time function for each city.
---------- Agent ----------
[FunctionCall(id='toolu_01NwP3fNAwcYKn1x656Dq9xW', arguments='{"city": "Paris"}', name='get_current_time'), FunctionCall(id='toolu_018d4cWSy3TxXhjgmLYFrfRt', arguments='{"city": "Toronto"}', name='get_current_time')]
---------- Agent ----------
[FunctionExecutionResult(content='The current time in Paris is 1:10.', call_id='toolu_01NwP3fNAwcYKn1x656Dq9xW', is_error=False), FunctionExecutionResult(content='The current time in Toronto is 7:28.', call_id='toolu_018d4cWSy3TxXhjgmLYFrfRt', is_error=False)]
---------- Agent ----------
The current time in Paris is 1:10.
The current time in Toronto is 7:28.
```

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-21 13:58:32 -08:00
Victor Dibia 45c6d133c2
Update AGS, Remove Numpy Dep (#5648)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

Update AGS, Remove Numpy Dep

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5639

## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-21 15:33:15 -05:00
Ryan Sweet 3a239c5336
message registry buffer size limit #5582 (#5603) 2025-02-21 09:23:21 -08:00
Victor Dibia 2f43005624
Improve Gallery Editor UX in AGS (#5613)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed? 

Fixes the problem where Gallery items could only be modified via JSON 

This PR does the following

- Refactor TeamBuilder to have modular component editor UI primarily
focused on editing each component type.
- Refactor the Gallery UX 
   - improve layout to use tabs for each component type 
- enable editing of each component item by reusing the component editor
- Enable switching between form editing and UI editing for coponent
editor view

This way, gallery items can be readily modified and then reused in the
component library in team builder.
It also implements an upate to the Gallery data structure to make it
more intuitive - it has a components field that has teams, agents,
models ...

<img width="1598" alt="image"
src="https://github.com/user-attachments/assets/3c3a228a-0bd2-4fc1-85ec-c9685c80bf72"
/>
<img width="1614" alt="image"
src="https://github.com/user-attachments/assets/5b6ed840-9c48-47bc-8c17-2aa50c7dcb99"
/>


<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

Closes #5465 
Closes #5047
 
cc @nour-bouzid @balakreshnan @EItanya @joslat @IustinT @leonG7
## Checks

- [ ] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-20 21:51:05 -08:00
Wei Jen Lu 34d30b56f5
Fix typo in doc (#5628)
Fix typo in doc

<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-20 14:07:44 -05:00
Ryan Sweet 7e1f7a762a
more dotnet doc improvements (#5559)
cleaning up the dotnet docs site, improving formatting, making the
landing page look more like the python
## Why are these changes needed?

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-19 17:24:26 +00:00
Ryan Sweet fa40568210
minor updates to the .NET docs (#5552)
## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number
2025-02-19 16:45:27 +00:00
OndeVai df829a133f
Fixing grammar issues (#5537)
Fixing grammar issues

<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-19 15:38:58 +00:00
gagb fa3396e9c3
Initialize BaseGroupChat before reset (#5608)
Fixes #5366 

Solution: Instead of raising an error called `_init()`

---------

Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com>
2025-02-19 15:03:54 +00:00
Li Jiang a0e3a1208c
Improve the model mismatch warning msg (#5586) 2025-02-19 01:17:41 +00:00
Eric Zhu 083129045c
feat: enhance issue templates with detailed guidance (#5594) 2025-02-19 01:07:24 +00:00
peterychang 2842c76aeb
Ollama client docs (#5605)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Adds ollama client documentation to the docs page

## Related issue number

https://github.com/microsoft/autogen/issues/5604

## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-18 16:25:51 -05:00
peterychang 4959b24777
Fix ollama docstring (#5600)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

Initial commit's docstrings were incorrect, which would be confusing for
a user

## Related issue number

https://github.com/microsoft/autogen/issues/5595
2025-02-18 13:38:35 -05:00
peterychang 8294c4c65e
Ollama client (#5553)
## Why are these changes needed?

Adds a client for ollama models

## Related issue number

https://github.com/microsoft/autogen/issues/5595

## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-18 11:39:34 -05:00
Victor Dibia e02db84586
Improves editing UI for tools in AGS (#5539)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->
<img width="1560" alt="image"
src="https://github.com/user-attachments/assets/da3d781f-2572-4bd7-802d-1d3900f6c6d9"
/>

## Why are these changes needed?

Improves  editing UI for tools in AGS
- add remove tools 
- add/remove imports 
- show source code section of FunctionTool in  in code editor

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-18 01:24:43 +00:00
Ryan Sweet 9a10427a7d
bugfix exception issue 5580 (#5581)
## Why are these changes needed?

fixing intermittent bug #5580 

was using the wrong scheduler. 

## Related issue number

closes #5580
2025-02-17 15:46:22 -08:00
Reece Adamson 17888819c2
doc: fix typo (recpients -> recipients) (#5570)
## Why are these changes needed?

Just fixing a very minor typo I noticed while reading the docs.

`recpients` -> `recipients`
2025-02-17 16:17:29 +00:00
Ryan Sweet 48242f5255
adding initial docker support for agenthost (#5479)
<!-- Thank you for your contribution! Please review
https://microsoft.github.io/autogen/docs/Contribute before opening a
pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR.
If you don't have the access to it, we will shortly find a reviewer and
assign them to your PR. -->

## Why are these changes needed?

<!-- Please give a short summary of the change and the problem this
solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [ ] I've included any doc changes needed for
https://microsoft.github.io/autogen/. See
https://microsoft.github.io/autogen/docs/Contribute#documentation to
build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [ ] I've made sure all auto checks have passed.
2025-02-17 16:07:19 +00:00
Ryan Sweet 2a90731c44
improving doc comments (#5550)
adding some missing doc comments
2025-02-17 16:01:48 +00:00
Eric Zhu c76a68c780
Update website version (#5561) 2025-02-15 01:30:32 -08:00
561 changed files with 54141 additions and 12510 deletions

View File

@ -1,5 +1,5 @@
# Note: You can use any Debian/Ubuntu based image you want.
FROM mcr.microsoft.com/devcontainers/universal:2
FROM mcr.microsoft.com/devcontainers/base:ubuntu
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \

View File

@ -20,7 +20,10 @@
},
"ghcr.io/elanhasson/devcontainer-features/dotnet-aspire-daily:1": {},
"ghcr.io/devcontainers/features/azure-cli:1": {},
"ghcr.io/azure/azure-dev/azd:0": {}
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/dotnet:2": {},
"ghcr.io/azure/azure-dev/azd:0": {},
"ghcr.io/devcontainers/features/python:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

171
.github/ISSUE_TEMPLATE/1-bug_report.yml vendored Normal file
View File

@ -0,0 +1,171 @@
name: 🐛 Bug Report
description: Report a bug
type: "bug"
labels:
- needs-triage
body:
- type: markdown
attributes:
value: |
## Please Read the following before submitting an issue.
### Have you read the docs?
- [Python AgentChat User Guide and Tutorial](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/index.html)
- [Python Core API User Guide](https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/index.html)
- [Python API Doc](https://microsoft.github.io/autogen/stable/reference/index.html)
- [.NET Doc](https://microsoft.github.io/autogen/dotnet/)
### Have you searched for related issues?
- Some other users might have the same issue as yours.
### Are you familiar with GitHub Markdown Syntax?
Please use [GitHub Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
syntax to format your input.
Pay attention to code blocks. Use "```" blocks for code and output.
For examples:
````
```python
# your Python code here.
```
````
````
```bash
# your bash shell command here.
```
````
If your output contains "```", use "````" (four "`") to escape them.
You can use the "Preview" switcher to check your formatted output.
- type: textarea
attributes:
label: What happened?
description: Please provide as much information as possible, this helps us address the issue. Use Markdown to format your text.
value: |
**Describe the bug**
A clear and concise description of what the bug is.
If it is a question or suggestion, please use [Discussions](https://github.com/microsoft/autogen/discussions)
instead.
**To Reproduce**
Steps to reproduce the behavior. Please include code and outputs such as stacktrace.
- If your input is just "I tried X, and it didn't work" or
"X is not working", your issue will be ignored.
- If your input is not well formatted, it will hurt readability and
may be ignored as well.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
validations:
required: true
- type: dropdown
attributes:
label: Which packages was the bug in?
multiple: true
options:
- Python Core (autogen-core)
- Python AgentChat (autogen-agentchat>=0.4.0)
- Python Extensions (autogen-ext)
- .NET Core (Microsoft.AutoGen.Core)
- AutoGen Studio (autogensudio)
- AutoGen Bench (agbench)
- Magentic One CLI (magentic-one-cli)
- V0.2 (autogen-agetnchat==0.2.*)
validations:
required: true
- type: dropdown
attributes:
label: AutoGen library version.
description: What is the version of the library was used.
multiple: false
options:
- "Python dev (main branch)"
- "Python 0.5.2"
- "Python 0.5.1"
- "Python 0.4.9"
- "Python 0.4.8"
- "Python 0.4.7"
- "Python 0.4.6"
- "Python 0.4.5"
- "Python 0.4.4"
- "Python 0.4.3"
- "Python 0.4.2"
- "Python 0.4.1"
- "Python 0.4.0"
- ".NET dev (main branch)"
- "Studio 0.4.1"
- "Studio 0.4.0"
- "Other (please specify)"
validations:
required: True
- type: input
attributes:
label: Other library version.
description: "Please specify if selected 'Other' above"
- type: input
attributes:
label: Model used
description: If a model was used, please name here. Use full model name with version number.
placeholder: "e.g., gpt-4o-2024-11-20"
- type: dropdown
attributes:
label: Model provider
description: The provider or hosting service that runs the model.
options:
- "Anthropic"
- "AWS Bedrock"
- "Azure OpenAI"
- "Azure AI Foundary (Azure AI Studio)"
- "DeepSeek (Hosted)"
- "GitHub Models"
- "Google Gemini"
- "Google Vertex AI"
- "HuggingFace Models (Hosted)"
- "HuggingFace Transformers (Local)"
- "LlamaCpp"
- "Mistral AI"
- "Ollama"
- "OpenAI"
- "OpenRouter"
- "Together AI"
- "vLLM"
- "Other (please specify below)"
- type: input
attributes:
label: Other model provider
description: "Other provider if not found above."
- type: dropdown
attributes:
label: Python version
options:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- Other (please note we only support Python 3.10+)
- type: dropdown
attributes:
label: .NET version
options:
- ".NET 9"
- ".NET 8"
- type: dropdown
attributes:
label: Operating system
options:
- Windows
- MacOS
- Ubuntu
- Fedora
- CentOS
- Other

47
.github/ISSUE_TEMPLATE/2-doc_issue.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: 📘 Doc Issue
description: Report an issue in the documentation, including missing or incorrect information.
type: "bug"
labels:
- needs-triage
- documentation
body:
- type: markdown
attributes:
value: |
## Please Read the following before submitting an issue.
### Have you read the docs?
- [Python AgentChat User Guide and Tutorial](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/index.html)
- [Python Core API User Guide](https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/index.html)
- [Python API Doc](https://microsoft.github.io/autogen/stable/reference/index.html)
- [.NET Doc](https://microsoft.github.io/autogen/dotnet/)
### Have you searched for related issues?
- Some other users might have the same issue as yours.
- type: textarea
attributes:
label: What is the doc issue?
description: Please provide as much information as possible, this helps us address the issue. Use Markdown to format your text.
value: |
**Describe the issue**
A clear and concise description of what the issue is. What is missing or incorrect?
**What do you want to see in the doc?**
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
validations:
required: true
- type: input
id: doc-link
attributes:
label: Link to the doc page, if applicable
description: Please provide a link to the doc page that has the issue.
placeholder: https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/index.html
validations:
required: true

View File

@ -0,0 +1,20 @@
name: 🔒 Maintainer Only
description: Only use this template if you are a maintainer.
body:
- type: checkboxes
attributes:
label: Confirmation
description: Please only use this template if you are a maintainer. Thanks for helping us keep the issue tracker organized!
options:
- label: I confirm that I am a maintainer and so can use this template. If I am not, I understand this issue will be closed and I will be asked to use a different template.
required: true
- type: textarea
id: body
attributes:
label: Issue body
description: "How do you trigger this bug? Please walk us through it step by step."
validations:
required: true

View File

@ -1,55 +0,0 @@
name: Bug Report
description: Report a bug
type: "bug"
body:
- type: textarea
attributes:
label: What happened?
description: Please provide as much information as possible, this helps us address the issue.
validations:
required: true
- type: textarea
attributes:
label: What did you expect to happen?
validations:
required: true
- type: textarea
attributes:
label: How can we reproduce it (as minimally and precisely as possible)?
description: Please provide steps to reproduce. Provide code that can be run if possible.
validations:
required: true
- type: input
attributes:
label: AutoGen version
description: What version or commit of the library was used
validations:
required: true
- type: dropdown
attributes:
label: Which package was this bug in
options:
- Core
- AgentChat
- Extensions
- AutoGen Studio
- Magentic One
- AutoGen Bench
- Other
validations:
required: true
- type: input
attributes:
label: Model used
description: If a model was used, please describe it here, indicating whether it is a local model or a cloud-hosted model
placeholder: gpt-4, mistral-7B etc
- type: input
attributes:
label: Python version
- type: input
attributes:
label: Operating system
- type: textarea
attributes:
label: Any additional info you think would be helpful for fixing this bug

View File

@ -1,5 +1,8 @@
blank_issues_enabled: true
blank_issues_enabled: false
contact_links:
- name: Questions or general help 💬
- name: 💬 Questions or general help
url: https://github.com/microsoft/autogen/discussions
about: Please ask and answer questions here.
- name: 💡 Suggest a new feature
url: https://github.com/microsoft/autogen/discussions/categories/feature-suggestions
about: Please suggest new features here and once the feature is accepted a maintainer will create an issue.

View File

@ -1,18 +0,0 @@
name: Feature Request
description: Request a new feature or enhancement
type: "feature"
body:
- type: textarea
attributes:
label: What feature would you like to be added?
description: Please describe the desired feature. Be descriptive, provide examples and if possible, provide a proposed solution.
validations:
required: true
- type: textarea
attributes:
label: Why is this needed?
description: Why is it important that this feature is implemented? What problem or need does it solve?
validations:
required: true

View File

@ -12,6 +12,6 @@
## Checks
- [ ] I've included any doc changes needed for https://microsoft.github.io/autogen/. See https://microsoft.github.io/autogen/docs/Contribute#documentation to build and test documentation locally.
- [ ] I've included any doc changes needed for <https://microsoft.github.io/autogen/>. See <https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to build and test documentation locally.
- [ ] I've added tests (if relevant) corresponding to the changes introduced in this PR.
- [ ] I've made sure all auto checks have passed.

View File

@ -181,6 +181,44 @@ jobs:
name: coverage-autogen-ext-grpc
path: ./python/coverage_autogen-ext-grpc.xml
test-autogen-ext-pwsh:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
version: "0.5.18"
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python deps
run: |
uv sync --locked --all-extras
shell: pwsh
working-directory: ./python
- name: Run tests for Windows
run: |
.venv/Scripts/activate.ps1
poe --directory ./packages/autogen-ext test-windows
shell: pwsh
working-directory: ./python
- name: Move coverage file
run: |
mv ./packages/autogen-ext/coverage.xml coverage_autogen_ext_windows.xml
working-directory: ./python
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
name: coverage-autogen-ext-windows
path: ./python/coverage_autogen_ext_windows.xml
codecov:
runs-on: ubuntu-latest
needs: [test, test-grpc]

View File

@ -33,7 +33,7 @@ jobs:
[
# For main use the workflow target
{ ref: "${{github.ref}}", dest-dir: dev, uv-version: "0.5.13", sphinx-release-override: "dev" },
{ ref: "python-v0.4.6", dest-dir: stable, uv-version: "0.5.13", sphinx-release-override: "stable" },
{ ref: "python-v0.5.2", dest-dir: stable, uv-version: "0.5.13", sphinx-release-override: "stable" },
{ ref: "v0.4.0.post1", dest-dir: "0.4.0", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "v0.4.1", dest-dir: "0.4.1", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "v0.4.2", dest-dir: "0.4.2", uv-version: "0.5.13", sphinx-release-override: "" },
@ -41,6 +41,11 @@ jobs:
{ ref: "v0.4.4", dest-dir: "0.4.4", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.4.5", dest-dir: "0.4.5", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.4.6", dest-dir: "0.4.6", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.4.7", dest-dir: "0.4.7", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.4.8", dest-dir: "0.4.8", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.4.9-website", dest-dir: "0.4.9", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.5.1", dest-dir: "0.5.1", uv-version: "0.5.13", sphinx-release-override: "" },
{ ref: "python-v0.5.2", dest-dir: "0.5.2", uv-version: "0.5.13", sphinx-release-override: "" },
]
steps:
- name: Checkout

View File

@ -1,18 +0,0 @@
name: Label issues with needs-triage
on:
issues:
types:
- reopened
- opened
jobs:
label_issues:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- run: gh issue edit "$NUMBER" --add-label "$LABELS"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
LABELS: needs-triage

5
.gitignore vendored
View File

@ -198,4 +198,7 @@ notebook/coding
artifacts
# project data
registry.json
registry.json
# files created by the gitty agent in python/samples/gitty
.gitty/

View File

@ -34,7 +34,7 @@ For common tasks that are helpful during development and run in CI, see [here](.
## Roadmap
We use GitHub issues and milestones to track our roadmap. You can view the upcoming milestones [here]([Roadmap Issues](https://aka.ms/autogen-roadmap).
We use GitHub issues and milestones to track our roadmap. You can view the upcoming milestones [here]([Roadmap Issues](https://aka.ms/autogen-roadmap)).
## Versioning
@ -48,11 +48,11 @@ We will update verion numbers according to the following rules:
## Release process
1. Create a PR that updates the version numbers across the codebase ([example](https://github.com/microsoft/autogen/pull/4359))
2. The docs CI will fail for the PR, but this is expected and will be resolved in the next step
2. After merging the PR, create and push a tag that corresponds to the new verion. For example, for `0.4.0.dev13`:
2. The docs CI will fail for the PR, but this is expected and will be resolved in the next step
3. After merging the PR, create and push a tag that corresponds to the new verion. For example, for `0.4.0.dev13`:
- `git tag v0.4.0.dev13 && git push origin v0.4.0.dev13`
3. Restart the docs CI by finding the failed [job corresponding to the `push` event](https://github.com/microsoft/autogen/actions/workflows/docs.yml) and restarting all jobs
4. Run [this](https://github.com/microsoft/autogen/actions/workflows/single-python-package.yml) workflow for each of the packages that need to be released and get an approval for the release for it to run
4. Restart the docs CI by finding the failed [job corresponding to the `push` event](https://github.com/microsoft/autogen/actions/workflows/docs.yml) and restarting all jobs
5. Run [this](https://github.com/microsoft/autogen/actions/workflows/single-python-package.yml) workflow for each of the packages that need to be released and get an approval for the release for it to run
## Triage process

View File

@ -47,22 +47,24 @@ from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
async def main() -> None:
agent = AssistantAgent("assistant", OpenAIChatCompletionClient(model="gpt-4o"))
model_client = OpenAIChatCompletionClient(model="gpt-4o")
agent = AssistantAgent("assistant", model_client=model_client)
print(await agent.run(task="Say 'Hello World!'"))
await model_client.close()
asyncio.run(main())
```
### Team
### Web Browsing Agent Team
Create a group chat team with an assistant agent, a web surfer agent, and a user proxy agent
Create a group chat team with a web surfer agent and a user proxy agent
for web browsing tasks. You need to install [playwright](https://playwright.dev/python/docs/library).
```python
# pip install -U autogen-agentchat autogen-ext[openai,web-surfer]
# playwright install
import asyncio
from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
from autogen_agentchat.agents import UserProxyAgent
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
@ -71,12 +73,21 @@ from autogen_ext.agents.web_surfer import MultimodalWebSurfer
async def main() -> None:
model_client = OpenAIChatCompletionClient(model="gpt-4o")
assistant = AssistantAgent("assistant", model_client)
web_surfer = MultimodalWebSurfer("web_surfer", model_client)
# The web surfer will open a Chromium browser window to perform web browsing tasks.
web_surfer = MultimodalWebSurfer("web_surfer", model_client, headless=False, animate_actions=True)
# The user proxy agent is used to get user input after each step of the web surfer.
# NOTE: you can skip input by pressing Enter.
user_proxy = UserProxyAgent("user_proxy")
termination = TextMentionTermination("exit") # Type 'exit' to end the conversation.
team = RoundRobinGroupChat([web_surfer, assistant, user_proxy], termination_condition=termination)
await Console(team.run_stream(task="Find information about AutoGen and write a short summary."))
# The termination condition is set to end the conversation when the user types 'exit'.
termination = TextMentionTermination("exit", sources=["user_proxy"])
# Web surfer and user proxy take turns in a round-robin fashion.
team = RoundRobinGroupChat([web_surfer, user_proxy], termination_condition=termination)
try:
# Start the team and wait for it to terminate.
await Console(team.run_stream(task="Find information about AutoGen and write a short summary."))
finally:
await web_surfer.close()
await model_client.close()
asyncio.run(main())
```
@ -101,7 +112,7 @@ The AutoGen ecosystem provides everything you need to create AI agents, especial
The _framework_ uses a layered and extensible design. Layers have clearly divided responsibilities and build on top of layers below. This design enables you to use the framework at different levels of abstraction, from high-level APIs to low-level components.
- [Core API](./python/packages/autogen-core/) implements message passing, event-driven agents, and local and distributed runtime for flexibility and power. It also support cross-language support for .NET and Python.
- [AgentChat API](./python/packages/autogen-agentchat/) implements a simpler but opinionated API rapid for prototyping. This API is built on top of the Core API and is closest to what users of v0.2 are familiar with and supports familiar multi-agent patterns such as two-agent chat or group chats.
- [AgentChat API](./python/packages/autogen-agentchat/) implements a simpler but opinionated API for rapid prototyping. This API is built on top of the Core API and is closest to what users of v0.2 are familiar with and supports common multi-agent patterns such as two-agent chat or group chats.
- [Extensions API](./python/packages/autogen-ext/) enables first- and third-party extensions continuously expanding framework capabilities. It support specific implementation of LLM clients (e.g., OpenAI, AzureOpenAI), and capabilities such as code execution.
The ecosystem also supports two essential _developer tools_:
@ -113,7 +124,7 @@ The ecosystem also supports two essential _developer tools_:
- [AutoGen Studio](./python/packages/autogen-studio/) provides a no-code GUI for building multi-agent applications.
- [AutoGen Bench](./python/packages/agbench/) provides a benchmarking suite for evaluating agent performance.
You can use the AutoGen framework and developer tools to create applications for your domain. For example, [Magentic-One](./python/packages/magentic-one-cli/) is a state-of-art multi-agent team built using AgentChat API and Extensions API that can handle variety of tasks that require web browsing, code execution, and file handling.
You can use the AutoGen framework and developer tools to create applications for your domain. For example, [Magentic-One](./python/packages/magentic-one-cli/) is a state-of-the-art multi-agent team built using AgentChat API and Extensions API that can handle a variety of tasks that require web browsing, code execution, and file handling.
With AutoGen you get to join and contribute to a thriving ecosystem. We host weekly office hours and talks with maintainers and community. We also have a [Discord server](https://aka.ms/autogen-discord) for real-time chat, GitHub Discussions for Q&A, and a blog for tutorials and updates.
@ -123,15 +134,14 @@ With AutoGen you get to join and contribute to a thriving ecosystem. We host wee
| | [![Python](https://img.shields.io/badge/AutoGen-Python-blue?logo=python&logoColor=white)](./python) | [![.NET](https://img.shields.io/badge/AutoGen-.NET-green?logo=.net&logoColor=white)](./dotnet) | [![Studio](https://img.shields.io/badge/AutoGen-Studio-purple?logo=visual-studio&logoColor=white)](./python/packages/autogen-studio) |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Installation | [![Installation](https://img.shields.io/badge/Install-blue)](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/installation.html) | \* | [![Install](https://img.shields.io/badge/Install-purple)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/installation.html) |
| Quickstart | [![Quickstart](https://img.shields.io/badge/Quickstart-blue)](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/quickstart.html#) | \* | [![Usage](https://img.shields.io/badge/Quickstart-blue)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
| Tutorial | [![Tutorial](https://img.shields.io/badge/Tutorial-blue)](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/index.html) | \* | [![Usage](https://img.shields.io/badge/Quickstart-blue)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
| API Reference | [![API](https://img.shields.io/badge/Docs-blue)](https://microsoft.github.io/autogen/stable/reference/index.html#) | \* | [![API](https://img.shields.io/badge/Docs-purple)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html) |
| Packages | [![PyPi autogen-core](https://img.shields.io/badge/PyPi-autogen--core-blue?logo=pypi)](https://pypi.org/project/autogen-core/) <br> [![PyPi autogen-agentchat](https://img.shields.io/badge/PyPi-autogen--agentchat-blue?logo=pypi)](https://pypi.org/project/autogen-agentchat/) <br> [![PyPi autogen-ext](https://img.shields.io/badge/PyPi-autogen--ext-blue?logo=pypi)](https://pypi.org/project/autogen-ext/) | \* | [![PyPi autogenstudio](https://img.shields.io/badge/PyPi-autogenstudio-purple?logo=pypi)](https://pypi.org/project/autogenstudio/) |
| Installation | [![Installation](https://img.shields.io/badge/Install-blue)](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/installation.html) | [![Install](https://img.shields.io/badge/Install-green)](https://microsoft.github.io/autogen/dotnet/dev/core/installation.html) | [![Install](https://img.shields.io/badge/Install-purple)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/installation.html) |
| Quickstart | [![Quickstart](https://img.shields.io/badge/Quickstart-blue)](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/quickstart.html#) | [![Quickstart](https://img.shields.io/badge/Quickstart-green)](https://microsoft.github.io/autogen/dotnet/dev/core/index.html) | [![Usage](https://img.shields.io/badge/Quickstart-purple)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
| Tutorial | [![Tutorial](https://img.shields.io/badge/Tutorial-blue)](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/index.html) | [![Tutorial](https://img.shields.io/badge/Tutorial-green)](https://microsoft.github.io/autogen/dotnet/dev/core/tutorial.html) | [![Usage](https://img.shields.io/badge/Tutorial-purple)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
| API Reference | [![API](https://img.shields.io/badge/Docs-blue)](https://microsoft.github.io/autogen/stable/reference/index.html#) | [![API](https://img.shields.io/badge/Docs-green)](https://microsoft.github.io/autogen/dotnet/dev/api/Microsoft.AutoGen.Contracts.html) | [![API](https://img.shields.io/badge/Docs-purple)](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html) |
| Packages | [![PyPi autogen-core](https://img.shields.io/badge/PyPi-autogen--core-blue?logo=pypi)](https://pypi.org/project/autogen-core/) <br> [![PyPi autogen-agentchat](https://img.shields.io/badge/PyPi-autogen--agentchat-blue?logo=pypi)](https://pypi.org/project/autogen-agentchat/) <br> [![PyPi autogen-ext](https://img.shields.io/badge/PyPi-autogen--ext-blue?logo=pypi)](https://pypi.org/project/autogen-ext/) | [![NuGet Contracts](https://img.shields.io/badge/NuGet-Contracts-green?logo=nuget)](https://www.nuget.org/packages/Microsoft.AutoGen.Contracts/) <br> [![NuGet Core](https://img.shields.io/badge/NuGet-Core-green?logo=nuget)](https://www.nuget.org/packages/Microsoft.AutoGen.Core/) <br> [![NuGet Core.Grpc](https://img.shields.io/badge/NuGet-Core.Grpc-green?logo=nuget)](https://www.nuget.org/packages/Microsoft.AutoGen.Core.Grpc/) <br> [![NuGet RuntimeGateway.Grpc](https://img.shields.io/badge/NuGet-RuntimeGateway.Grpc-green?logo=nuget)](https://www.nuget.org/packages/Microsoft.AutoGen.RuntimeGateway.Grpc/) | [![PyPi autogenstudio](https://img.shields.io/badge/PyPi-autogenstudio-purple?logo=pypi)](https://pypi.org/project/autogenstudio/) |
</div>
\*_Releasing soon_
Interested in contributing? See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on how to get started. We welcome contributions of all kinds, including bug fixes, new features, and documentation improvements. Join our community and help us make AutoGen better!

View File

@ -11,7 +11,7 @@ Each event in the system is defined using the [CloudEvents Specification](https:
1. *id* - A unique id (eg. a UUID).
2. *source* - A URI or URN indicating the event's origin.
3. *type* - The namespace of the event - prefixed with a reverse-DNS name.
- The prefixed domain dictates the organization which defines the semantics of this event type: e.g `com.github.pull_request.opened` or `com.example.object.deleted.v2`), and optionally fields describing the data schema/content-type or extensions.
- The prefixed domain dictates the organization which defines the semantics of this event type: e.g (`com.github.pull_request.opened` or `com.example.object.deleted.v2`), and optionally fields describing the data schema/content-type or extensions.
## Event Handlers

View File

@ -62,7 +62,7 @@ For this subscription source should map directly to agent key.
This subscription will therefore receive all events for the following well known topics:
- `{AgentType}:` - General purpose direct messages. These should be routed to the approriate message handler.
- `{AgentType}:rpc_request={RequesterAgentType}` - RPC request messages. These should be routed to the approriate RPC handler, and RequesterAgentType used to publish the response
- `{AgentType}:` - General purpose direct messages. These should be routed to the appropriate message handler.
- `{AgentType}:rpc_request={RequesterAgentType}` - RPC request messages. These should be routed to the appropriate RPC handler, and RequesterAgentType used to publish the response
- `{AgentType}:rpc_response={RequestId}` - RPC response messages. These should be routed back to the response future of the caller.
- `{AgentType}:error={RequestId}` - Error message that corresponds to the given request.

View File

@ -1,13 +1,16 @@
## How to build and run the website
# How to build and run the website
### Prerequisites
- dotnet 7.0 or later
## Prerequisites
- dotnet 8.0 or later
## Build
### Build
Firstly, go to autogen/dotnet folder and run the following command to build the website:
```bash
dotnet tool restore
dotnet tool run docfx website/docfx.json --serve
dotnet tool run docfx ../docs/dotnet/docfx.json --serve
```
After the command is executed, you can open your browser and navigate to `http://localhost:8080` to view the website.
After the command is executed, you can open your browser and navigate to `http://localhost:8080` to view the website.

View File

@ -7,11 +7,15 @@ Any important differences between the language versions are documented in the [D
## Getting Started
You can obtain the SDK as a nuget package or by cloning the repository. The SDK is available on [NuGet](https://www.nuget.org/packages/Microsoft.AutoGen).
Minimally you will need the following:
```bash
dotnet add package Microsoft.AutoGen.Contracts
dotnet add package Microsoft.AutoGen.Core
```
See [Installation](./installation.md) for more detailed notes on installing all the related packages.
You can quickly get started by looking at the samples in the [samples](https://github.com/microsoft/autogen/tree/main/dotnet/samples) directory of the repository.
### Creating an Agent
@ -74,7 +78,7 @@ dotnet add package Microsoft.AutoGen.AgentHost
You can run the backend on its own:
```bash
dotnet run --project Microsoft.AutoGen.RuntimeGateway
dotnet run --project Microsoft.AutoGen.AgentHost
```
or you can run iclude it inside your own application:
@ -85,6 +89,17 @@ using Microsoft.AutoGen.AgentHost;
var autogenBackend = await Microsoft.AutoGen.RuntimeGateway.Grpc.Host.StartAsync(local: false, useGrpc: true).ConfigureAwait(false);
```
You can also install the runtime as a dotnet tool:
```
dotnet pack --no-build --configuration Release --output './output/release' -bl\n
dotnet tool install --add-source ./output/release Microsoft.AutoGen.AgentHost
# run the tool
# dotnet agenthost
# or just...
agenthost
```
### Running Multiple Agents and the Runtime in separate processes with .NET Aspire
The [Hello.AppHost project](https://github.com/microsoft/autogen/blob/50d7587a4649504af3bb79ab928b2a3882a1a394/dotnet/samples/Hello/Hello.AppHost/Program.cs#L4) illustrates how to orchestrate a distributed system with multiple agents and the runtime in separate processes using .NET Aspire. It also points to a [python agent that illustrates how to run agents in different languages in the same distributed system](https://github.com/microsoft/autogen/blob/50d7587a4649504af3bb79ab928b2a3882a1a394/python/samples/core_xlang_hello_python_agent/README.md#L1).
@ -117,6 +132,8 @@ Console.WriteLine("Backend URL: " + url);
await app.WaitForShutdownAsync();
```
You can find more examples of how to use Aspire and XLang agents in the [Microsoft.AutoGen.Integration.Tests.AppHost](https://github.com/microsoft/autogen/blob/acd7e864300e24a3ee67a89a916436e8894bb143/dotnet/test/Microsoft.AutoGen.Integration.Tests.AppHosts/) directory.
### Configuring Logging
The SDK uses the Microsoft.Extensions.Logging framework for logging. Here is an example appsettings.json file with some useful defaults:

View File

@ -20,3 +20,29 @@ Or, add via `<PackageReference>`
<PackageReference Include="Microsoft.AutoGen.Contracts" Version="0.4.0-dev.1" />
<PackageReference Include="Microsoft.AutoGen.Core" Version="0.4.0-dev.1" />
```
# Additional Packages
The *Core* and *Contracts* packages will give you what you need for writing and running agents using the Core API within a single process.
- *Microsoft.AutoGen.AgentChat* - An implementation of the AgentChat package for building chat-centric agent orchestration on top of the Core SDK
- *Microsoft.AutoGen.Agents* - a package that has a small number of default agents you can use.
- *Microsoft.AutoGen.Extensions* - Extensions to support closely related projects including Aspire, Microsoft.Extensions.AI, and Semantic Kernel
```sh
dotnet add package Microsoft.AutoGen.AgentChat --version 0.4.0-dev-1
dotnet add package Microsoft.AutoGen.Agents --version 0.4.0-dev-1
dotnet add package Microsoft.AutoGen.Extensions --version 0.4.0-dev-1
```
To enable running a system with agents in different processes that allows for x-language communication between python and .NET agents, there are additional packages:
- *Microsoft.AutoGen.Core.Grpc* - the .NET client runtime for agents in a distributed system. It has the same API as *Microsoft.AutoGen.Core*.
- *Microsoft.AutoGen.RuntimeGatewway.Grpc* - the .NET server side of the distributed system that allows you to run multiple gateways to manage fleets of agents and enables x-language interoperability.
- *Microsoft.AutoGen.AgentHost* - A .NET Aspire project that hosts the Grpc Service
```sh
dotnet add package Microsoft.AutoGen.Core.Grpc --version 0.4.0-dev-1
dotnet add package Microsoft.AutoGen.RuntimeGateway.Grpc --version 0.4.0-dev-1
dotnet add package Microsoft.AutoGen.AgentHost --version 0.4.0-dev-1
```

View File

@ -7,4 +7,4 @@
- name: Differences from Python
href: differences-from-python.md
- name: Protobuf message types
href: protobuf-message-types.md
href: protobuf-message-types.md

View File

@ -5,7 +5,11 @@
{
"files": [
"src/Microsoft.AutoGen/Core/**/*.csproj",
"src/Microsoft.AutoGen/Contracts/**/*.csproj"
"src/Microsoft.AutoGen/Contracts/**/*.csproj",
"src/Microsoft.AutoGen/Core.Grpc/**/*.csproj",
"src/Microsoft.AutoGen/AgentHost/**/*.csproj",
"src/Microsoft.AutoGen/RuntimeGateway.Grpc/**/*.csproj",
"src/Microsoft.AutoGen/Extensions/**/*.csproj"
],
"src": "../../dotnet/"
}
@ -69,4 +73,4 @@
"keepFileLink": false,
"disableGitFeatures": false
}
}
}

View File

@ -1,15 +1,6 @@
---
_disableAffix: true
---
<style>
.center {
text-align: center;
}
.subheader {
font-size: 1.5em;
}
</style>
<div class="center">
<h1>AutoGen .NET</h1>
@ -23,7 +14,46 @@ _disableAffix: true
<div class="card">
<div class="card-body">
<h5 class="card-title">Core</h5>
<p>
[![dotnet-ci](https://github.com/microsoft/autogen/actions/workflows/dotnet-build.yml/badge.svg)](https://github.com/microsoft/autogen/actions/workflows/dotnet-build.yml)
[![NuGet version](https://badge.fury.io/nu/Microsoft.AutoGen.Contracts.svg)](https://badge.fury.io/nu/Microsoft.AutoGen.Contracts)
[![NuGet version](https://badge.fury.io/nu/Microsoft.AutoGen.Core.svg)](https://badge.fury.io/nu/Microsoft.AutoGen.Core)
[![NuGet version](https://badge.fury.io/nu/Microsoft.AutoGen.Core.Grpc.svg)](https://badge.fury.io/nu/Microsoft.AutoGen.Core.Grpc)
[![NuGet version](https://badge.fury.io/nu/Microsoft.AutoGen.RuntimeGateway.Grpc.svg)](https://badge.fury.io/nu/Microsoft.AutoGen.RuntimeGateway.Grpc)
[![NuGet version](https://badge.fury.io/nu/Microsoft.AutoGen.AgentHost.svg)](https://badge.fury.io/nu/Microsoft.AutoGen.AgentHost)
</p>
<p class="card-text">An event-driven programming framework for building scalable multi-agent AI systems.</p>
- Deterministic and dynamic agentic workflows for business processes
- Research on multi-agent collaboration
- Distributed agents for multi-language applications
- integration with event-driven, cloud native applications
*Start here if you are building workflows or distributed agent systems*
<p>
<div class="highlight">
<pre id="codecell0" tabindex="0">
```bash
dotnet add package Microsoft.AutoGen.Contracts
dotnet add package Microsoft.AutoGen.Core
# optionally - for distributed agent systems:
dotnet add package Microsoft.AutoGen.RuntimeGateway.Grpc
dotnet add package Microsoft.AutoGen.AgentHost
# other optional packages
dotnet add package Microsoft.AutoGen.Agents
dotnet add package Microsoft.AutoGen.Extensions.Aspire
dotnet add package Microsoft.AutoGen.Extensions.MEAI
dotnet add package Microsoft.AutoGen.Extensions.SemanticKernel
```
</pre></div></p>
<p>
<a href="core/index.md" class="btn btn-primary">Get started</a>
</div>
</div>

View File

@ -2,3 +2,114 @@
height: 50px;
margin-right: 0.5rem;
}
.bd-footer {
font-size: 0.8rem;
}
html[data-theme="light"] {
--pst-color-primary: hsl(222.2 47.4% 11.2%);
--pst-color-secondary: #007bff;
--pst-color-secondary-bg: #007bff;
--pst-color-accent: #007bff;
--sd-color-secondary-highlight: #0062cc;
--pst-color-shadow: rgba(0, 0, 0, 0.0);
}
html[data-theme="dark"] {
--pst-color-primary: hsl(213 31% 91%);
--pst-color-secondary: #007bff;
--pst-color-secondary-bg: #007bff;
--pst-color-accent: #007bff;
--sd-color-secondary-highlight: #0062cc;
--pst-color-shadow: rgba(0, 0, 0, 0.0);
}
.bd-header-announcement {
color: white;
}
.bd-header-announcement a {
color: white;
}
.bd-header-announcement a:hover {
color: white;
text-shadow: 0.5px 0 0 currentColor;
}
nav.bd-links .current>a {
box-shadow: inset 1px 0 0 var(--pst-color-primary);
}
html[data-theme="light"] .bd-header {
border-bottom: 1px solid var(--pst-color-border);
}
.admonition, div.admonition {
border: 1px solid var(--pst-color-border);
}
.api-card {
text-align: center;
font-size: 1.2rem;
}
.api-card svg {
font-size: 2rem;
}
.search-button-field {
border-radius: var(--bs-btn-border-radius);
}
.bd-content .sd-tab-set .sd-tab-content {
border: none;
border-top: 3px solid var(--pst-color-border);
}
.bd-content .sd-tab-set>input:checked+label {
border: none;
transform: translateY(0);
font-weight: 600;
border-bottom: 2px solid var(--pst-color-secondary);
}
.bd-content .sd-tab-set>label {
background-color: transparent;
border: none;
}
.center {
text-align: center;
}
.subheader {
font-size: 1.5em;
}
.hero-title {
font-size: 60px;
font-weight: bold;
margin: 2rem auto 0;
}
.wip-card {
border: 1px solid var(--pst-color-success);
background-color: var(--pst-color-success-bg);
border-radius: .25rem;
padding: 0.3rem;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 1rem;
}
.card-title {
font-size: 1.2rem;
font-weight: bold;
}
.card-title svg {
font-size: 2rem;
vertical-align: bottom;
margin-right: 5px;
}

View File

@ -1,18 +1,73 @@
[
{
"name": "0.4.6 (stable)",
"version": "stable",
"url": "/autogen/stable/",
"preferred": true
},
{
"name": "dev (main)",
"version": "dev",
"url": "/autogen/dev/"
},
{
"name": "0.5.2 (stable)",
"version": "stable",
"url": "/autogen/stable/",
"preferred": true
},
{
"name": "0.5.1",
"version": "0.5.1",
"url": "/autogen/0.5.1/"
},
{
"name": "0.4.9",
"version": "0.4.9",
"url": "/autogen/0.4.9/"
},
{
"name": "0.4.8",
"version": "0.4.8",
"url": "/autogen/0.4.8/"
},
{
"name": "0.4.7",
"version": "0.4.7",
"url": "/autogen/0.4.7/"
},
{
"name": "0.4.6",
"version": "0.4.6",
"url": "/autogen/0.4.6/"
},
{
"name": "0.4.5",
"version": "0.4.5",
"url": "/autogen/0.4.5/"
},
{
"name": "0.4.4",
"version": "0.4.4",
"url": "/autogen/0.4.4/"
},
{
"name": "0.4.3",
"version": "0.4.3",
"url": "/autogen/0.4.3/"
},
{
"name": "0.4.2",
"version": "0.4.2",
"url": "/autogen/0.4.2/"
},
{
"name": "0.4.1",
"version": "0.4.1",
"url": "/autogen/0.4.1/"
},
{
"name": "0.4.0",
"version": "0.4.0",
"url": "/autogen/0.4.0/"
},
{
"name": "0.2",
"version": "0.2",
"url": "/autogen/0.2/"
}
]
]

View File

@ -6,13 +6,15 @@
"version": "0.1.205",
"commands": [
"dotnet-repl"
]
],
"rollForward": true
},
"docfx": {
"version": "2.67.5",
"commands": [
"docfx"
]
],
"rollForward": true
}
}
}

View File

@ -2,10 +2,12 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<MicrosoftSemanticKernelVersion>1.22.0</MicrosoftSemanticKernelVersion>
<MicrosoftSemanticKernelExperimentalVersion>1.22.0-alpha</MicrosoftSemanticKernelExperimentalVersion>
<MicrosoftExtensionsAIVersion>9.0.0-preview.9.24525.1</MicrosoftExtensionsAIVersion>
<MicrosoftSemanticKernelStableVersion>1.45.0</MicrosoftSemanticKernelStableVersion>
<MicrosoftSemanticKernelPreviewVersion>$(MicrosoftSemanticKernelStableVersion)-preview</MicrosoftSemanticKernelPreviewVersion>
<MicrosoftSemanticKernelAlphaVersion>$(MicrosoftSemanticKernelStableVersion)-alpha</MicrosoftSemanticKernelAlphaVersion>
<MicrosoftExtensionsAIVersion>9.3.0-preview.1.25161.3</MicrosoftExtensionsAIVersion>
<MicrosoftExtensionConfiguration>9.0.0</MicrosoftExtensionConfiguration>
<MicrosoftExtensionDependencyInjection>9.0.0</MicrosoftExtensionDependencyInjection>
<MicrosoftExtensionDependencyInjection>9.0.3</MicrosoftExtensionDependencyInjection>
<MicrosoftExtensionLogging>9.0.0</MicrosoftExtensionLogging>
<MicrosoftExtensionOptions>9.0.0</MicrosoftExtensionOptions>
<MicrosoftOrleans>9.0.1</MicrosoftOrleans>
@ -18,7 +20,7 @@
<PackageVersion Include="Aspire.Hosting" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Python" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="9.0.0" />
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="8.0.1-preview.8.24267.1" />
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="9.0.0-preview.5.24551.3" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="9.0.0" />
@ -27,9 +29,10 @@
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Redis" Version="8.2.0" />
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.1.0-beta.2" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.2.0-beta.4" />
<PackageVersion Include="Azure.AI.Inference" Version="1.0.0-beta.1" />
<PackageVersion Include="Azure.Data.Tables" Version="12.9.1" />
<PackageVersion Include="Azure.Core" Version="1.44.1" />
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" />
<PackageVersion Include="Azure.Storage.Files.Shares" Version="12.21.0" />
@ -100,18 +103,18 @@
<PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="$(MicrosoftOrleans)" />
<PackageVersion Include="Microsoft.Orleans.TestingHost" Version="9.0.1" />
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.5.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.29.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Agents.Core" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="1.29.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Web" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="$(MicrosoftSemanticKernelStableVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Agents.Core" Version="$(MicrosoftSemanticKernelStableVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="$(MicrosoftSemanticKernelStableVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="$(MicrosoftSemanticKernelPreviewVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="$(MicrosoftSemanticKernelAlphaVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Web" Version="$(MicrosoftSemanticKernelAlphaVersion)" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Octokit" Version="13.0.1" />
<PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.4.1" />
<PackageVersion Include="OpenAI" Version="2.1.0-beta.2" />
<PackageVersion Include="OpenAI" Version="2.2.0-beta.4" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="$(OpenTelemetryInstrumentation)" />
@ -127,7 +130,7 @@
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
<PackageVersion Include="System.IO.Packaging" Version="9.0.0" />
<PackageVersion Include="System.Memory.Data" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.3" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.console" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />

View File

@ -2,7 +2,7 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionPrefix>0.4.0</VersionPrefix>
<VersionPrefixForAutoGen0_2>0.2.2</VersionPrefixForAutoGen0_2>
<VersionPrefixForAutoGen0_2>0.2.3</VersionPrefixForAutoGen0_2>
<Authors>Microsoft</Authors>
<PackageProjectUrl>https://microsoft.github.io/autogen-for-net/</PackageProjectUrl>
<RepositoryUrl>https://github.com/microsoft/autogen</RepositoryUrl>

View File

@ -50,7 +50,7 @@ public class Example10_SemanticKernel
kernel.Plugins.AddFromObject(new LightPlugin());
var skAgent = kernel
.ToSemanticKernelAgent(name: "assistant", systemMessage: "You control the light", settings);
.ToSemanticKernelAgent(name: "assistant", systemMessage: "You control the light", settings: settings);
// Send a message to the skAgent, the skAgent supports the following message types:
// - IMessage<ChatMessageContent>

View File

@ -4,6 +4,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.AI;
@ -69,36 +71,48 @@ public class FunctionContract
/// </summary>
public string? ReturnDescription { get; set; }
public static implicit operator FunctionContract(AIFunctionMetadata metadata)
public static implicit operator FunctionContract(AIFunction function)
{
var openapiScheme = function.JsonSchema;
var parameters = new List<FunctionParameterContract>();
string[] isRequiredProperties = [];
if (openapiScheme.TryGetProperty("required", out var requiredElement))
{
isRequiredProperties = requiredElement.Deserialize<string[]>() ?? [];
}
var parameterList = function.UnderlyingMethod?.GetParameters() ?? Array.Empty<ParameterInfo>();
if (openapiScheme.TryGetProperty("properties", out var propertiesElement))
{
var properties = propertiesElement.Deserialize<Dictionary<string, JsonElement>>() ?? new Dictionary<string, JsonElement>();
foreach (var property in properties)
{
var parameterType = parameterList.FirstOrDefault(p => p.Name == property.Key)?.ParameterType;
var parameter = new FunctionParameterContract
{
Name = property.Key,
ParameterType = parameterType, // TODO: Need to get the type from the schema
IsRequired = isRequiredProperties.Contains(property.Key),
};
if (property.Value.TryGetProperty("description", out var descriptionElement))
{
parameter.Description = descriptionElement.GetString();
}
if (property.Value.TryGetProperty("default", out var defaultValueElement))
{
parameter.DefaultValue = defaultValueElement.Deserialize<object>();
}
parameters.Add(parameter);
}
}
return new FunctionContract
{
Namespace = metadata.AdditionalProperties.ContainsKey(NamespaceKey) ? metadata.AdditionalProperties[NamespaceKey] as string : null,
ClassName = metadata.AdditionalProperties.ContainsKey(ClassNameKey) ? metadata.AdditionalProperties[ClassNameKey] as string : null,
Name = metadata.Name,
Description = metadata.Description,
Parameters = metadata.Parameters?.Select(p => (FunctionParameterContract)p).ToList(),
ReturnType = metadata.ReturnParameter.ParameterType,
ReturnDescription = metadata.ReturnParameter.Description,
};
}
public static implicit operator AIFunctionMetadata(FunctionContract contract)
{
return new AIFunctionMetadata(contract.Name)
{
Description = contract.Description,
ReturnParameter = new AIFunctionReturnParameterMetadata()
{
Description = contract.ReturnDescription,
ParameterType = contract.ReturnType,
},
AdditionalProperties = new Dictionary<string, object?>
{
[NamespaceKey] = contract.Namespace,
[ClassNameKey] = contract.ClassName,
},
Parameters = [.. contract.Parameters?.Select(p => (AIFunctionParameterMetadata)p)!],
Namespace = function.AdditionalProperties.ContainsKey(NamespaceKey) ? function.AdditionalProperties[NamespaceKey] as string : null,
ClassName = function.AdditionalProperties.ContainsKey(ClassNameKey) ? function.AdditionalProperties[ClassNameKey] as string : null,
Name = function.Name,
Description = function.Description,
Parameters = parameters,
};
}
}
@ -132,29 +146,4 @@ public class FunctionParameterContract
/// The default value of the parameter.
/// </summary>
public object? DefaultValue { get; set; }
// convert to/from FunctionParameterMetadata
public static implicit operator FunctionParameterContract(AIFunctionParameterMetadata metadata)
{
return new FunctionParameterContract
{
Name = metadata.Name,
Description = metadata.Description,
ParameterType = metadata.ParameterType,
IsRequired = metadata.IsRequired,
DefaultValue = metadata.DefaultValue,
};
}
public static implicit operator AIFunctionParameterMetadata(FunctionParameterContract contract)
{
return new AIFunctionParameterMetadata(contract.Name!)
{
DefaultValue = contract.DefaultValue,
Description = contract.Description,
IsRequired = contract.IsRequired,
ParameterType = contract.ParameterType,
HasDefaultValue = contract.DefaultValue != null,
};
}
}

View File

@ -53,9 +53,9 @@ public class FunctionCallMiddleware : IStreamingMiddleware
public FunctionCallMiddleware(IEnumerable<AIFunction> functions, string? name = null)
{
this.Name = name ?? nameof(FunctionCallMiddleware);
this.functions = functions.Select(f => (FunctionContract)f.Metadata).ToArray();
this.functions = functions.Select(f => (FunctionContract)f).ToArray();
this.functionMap = functions.Select(f => (f.Metadata.Name, this.AIToolInvokeWrapper(f.InvokeAsync))).ToDictionary(f => f.Name, f => f.Item2);
this.functionMap = functions.Select(f => (f.Name, this.AIToolInvokeWrapper(f.InvokeAsync))).ToDictionary(f => f.Name, f => f.Item2);
}
public string? Name { get; }

View File

@ -8,9 +8,9 @@ namespace AutoGen.SemanticKernel.Extension;
public static class KernelExtension
{
public static SemanticKernelAgent ToSemanticKernelAgent(this Kernel kernel, string name, string systemMessage = "You are a helpful AI assistant", PromptExecutionSettings? settings = null)
public static SemanticKernelAgent ToSemanticKernelAgent(this Kernel kernel, string name, string systemMessage = "You are a helpful AI assistant", string? modelServiceId = null, PromptExecutionSettings? settings = null)
{
return new SemanticKernelAgent(kernel, name, systemMessage, settings);
return new SemanticKernelAgent(kernel, name, systemMessage, modelServiceId, settings);
}
/// <summary>

View File

@ -32,17 +32,28 @@ public class SemanticKernelAgent : IStreamingAgent
{
private readonly Kernel _kernel;
private readonly string _systemMessage;
private readonly string? _modelServiceId;
private readonly PromptExecutionSettings? _settings;
/// <summary>
/// Create a new instance of <see cref="SemanticKernelAgent"/>
/// </summary>
/// <param name="kernel">The Semantic Kernel - Kernel object</param>
/// <param name="name">The name of the agent.</param>
/// <param name="systemMessage">The system message.</param>
/// <param name="modelServiceId">Optional serviceId for the model.</param>
/// <param name="settings">The prompt execution settings.</param>
public SemanticKernelAgent(
Kernel kernel,
string name,
string systemMessage = "You are a helpful AI assistant",
string? modelServiceId = null,
PromptExecutionSettings? settings = null)
{
_kernel = kernel;
this.Name = name;
_systemMessage = systemMessage;
_modelServiceId = modelServiceId;
_settings = settings;
}
@ -52,7 +63,7 @@ public class SemanticKernelAgent : IStreamingAgent
{
var chatHistory = BuildChatHistory(messages);
var option = BuildOption(options);
var chatService = _kernel.GetRequiredService<IChatCompletionService>();
var chatService = GetChatCompletionService();
var reply = await chatService.GetChatMessageContentsAsync(chatHistory, option, _kernel, cancellationToken);
@ -71,7 +82,7 @@ public class SemanticKernelAgent : IStreamingAgent
{
var chatHistory = BuildChatHistory(messages);
var option = BuildOption(options);
var chatService = _kernel.GetRequiredService<IChatCompletionService>();
var chatService = GetChatCompletionService();
var response = chatService.GetStreamingChatMessageContentsAsync(chatHistory, option, _kernel, cancellationToken);
await foreach (var content in response)
@ -108,6 +119,13 @@ public class SemanticKernelAgent : IStreamingAgent
};
}
private IChatCompletionService GetChatCompletionService()
{
return string.IsNullOrEmpty(_modelServiceId)
? _kernel.GetRequiredService<IChatCompletionService>()
: _kernel.GetRequiredService<IChatCompletionService>(_modelServiceId);
}
private IEnumerable<ChatMessageContent> ProcessMessage(IEnumerable<IMessage> messages)
{
return messages.Select(m => m switch

View File

@ -26,8 +26,9 @@ public class SemanticKernelChatCompletionAgent : IAgent
public async Task<IMessage> GenerateReplyAsync(IEnumerable<IMessage> messages, GenerateReplyOptions? options = null,
CancellationToken cancellationToken = default)
{
ChatMessageContent[] reply = await _chatCompletionAgent
.InvokeAsync(BuildChatHistory(messages), cancellationToken: cancellationToken)
var agentThread = new ChatHistoryAgentThread(BuildChatHistory(messages));
var reply = await _chatCompletionAgent
.InvokeAsync(agentThread, cancellationToken: cancellationToken)
.ToArrayAsync(cancellationToken: cancellationToken);
return reply.Length > 1

View File

@ -2,6 +2,7 @@
// ChatAgent.cs
using System.Text.RegularExpressions;
using Microsoft.AutoGen.Contracts;
namespace Microsoft.AutoGen.AgentChat.Abstractions;
@ -165,7 +166,8 @@ public class ChatStreamFrame : StreamingFrame<Response, AgentMessage>;
/// </summary>
public interface IChatAgent :
IHandleChat<IEnumerable<ChatMessage>, Response>,
IHandleStream<IEnumerable<ChatMessage>, ChatStreamFrame>
IHandleStream<IEnumerable<ChatMessage>, ChatStreamFrame>,
ISaveState
{
/// <summary>
/// The name of the agent. This is used by team to uniquely identify the agent.It should be unique within the team.

View File

@ -1,12 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ITeam.cs
using Microsoft.AutoGen.Contracts;
namespace Microsoft.AutoGen.AgentChat.Abstractions;
/// <summary>
/// A team of agents.
/// </summary>
public interface ITeam : ITaskRunner
public interface ITeam : ITaskRunner, ISaveState
{
/// <summary>
/// Reset the team and all its participants to its initial state.

View File

@ -4,7 +4,10 @@
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Microsoft.AutoGen.AgentChat.GroupChat;
using Microsoft.Extensions.AI;
namespace Microsoft.AutoGen.AgentChat.Abstractions;
@ -22,6 +25,11 @@ public abstract class AgentMessage
/// </summary>
public required string Source { get; set; }
/// <summary>
/// The <see cref="IChatClient"/> usage incurred when producing this message.
/// </summary>
public RequestUsage? ModelUsage { get; set; }
// IMPORTANT NOTE: Unlike the ITypeMarshal<AgentMessage, WireProtocol.AgentMessage> implementation in ProtobufTypeMarshal,
// the .ToWire() call on this is intended to be used for directly converting a concrete message type to its leaf representation.
// In the context of Protobuf these may not be the same due to discriminated union types being real types, as opposed to
@ -116,7 +124,7 @@ public struct MultiModalData
/// <param name="item">The <see cref="AIContent"/> to wrap.</param>
/// <returns>A <see cref="MultiModalData"/> instance wrapping the <paramref name="item"/>.</returns>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="item"/> is not a <see cref="TextContent"/> or <see cref="ImageContent"/>.
/// Thrown if the <paramref name="item"/> is not a <see cref="TextContent"/> or <see cref="DataContent"/>.
/// </exception>
public static MultiModalData CheckTypeAndCreate(AIContent item)
{
@ -124,7 +132,7 @@ public struct MultiModalData
{
return new MultiModalData(text);
}
else if (item is ImageContent image)
else if (item is DataContent image)
{
return new MultiModalData(image);
}
@ -155,10 +163,10 @@ public struct MultiModalData
}
/// <summary>
/// Initializes a new instance of the <see cref="MultiModalData"/> with an <see cref="ImageContent"/>.
/// Initializes a new instance of the <see cref="MultiModalData"/> with an <see cref="DataContent"/>.
/// </summary>
/// <param name="image">The image to wrap.</param>
public MultiModalData(ImageContent image)
public MultiModalData(DataContent image)
{
ContentType = Type.Image;
AIContent = image;
@ -246,12 +254,12 @@ public class MultiModalMessage : ChatMessage, IList<AIContent>
}
/// <summary>
/// Adds a range of <see cref="ImageContent"/> to the message.
/// Adds a range of <see cref="DataContent"/> to the message.
/// </summary>
/// <param name="images">The items to add.</param>
public void AddRange(IEnumerable<ImageContent> images)
public void AddRange(IEnumerable<DataContent> images)
{
foreach (ImageContent image in images)
foreach (DataContent image in images)
{
this.Add(image);
}
@ -279,7 +287,7 @@ public class MultiModalMessage : ChatMessage, IList<AIContent>
/// Adds a <see cref="TextContent"/> to the message.
/// </summary>
/// <param name="image">The image to add.</param>
public void Add(ImageContent image)
public void Add(DataContent image)
{
this.Content.Add(new(image));
}
@ -366,7 +374,7 @@ public class MultiModalMessage : ChatMessage, IList<AIContent>
}
/// <inheritdoc cref="IList{ImageContent}.Insert(int, ImageContent)"/>
public void Insert(int index, ImageContent image)
public void Insert(int index, DataContent image)
{
this.Content.Insert(index, new(image));
}
@ -492,6 +500,11 @@ public class FunctionExecutionResult
/// </summary>
public required string Id { get; set; }
/// <summary>
/// The name of the function that was called.
/// </summary>
public required string Name { get; set; }
/// <summary>
/// The result of calling the function.
/// </summary>
@ -597,7 +610,7 @@ public static class CompletionChatMessageExtensions
{
contentBuilder.AppendLine(textContent.Text);
}
else if (content is ImageContent)
else if (content is DataContent)
{
contentBuilder.AppendLine("[Image]");
}
@ -614,3 +627,129 @@ public static class CompletionChatMessageExtensions
};
}
}
public static class MessageSerializationHelpers
{
internal sealed class TypeNode(Type type)
{
public Type Type { get; } = type;
public TypeNode? Parent { get; set; }
public TypeNode Root => this.Parent?.Root ?? this;
public List<TypeNode> Children { get; } = new List<TypeNode>();
public IEnumerable<Type> ChildrenTransitiveClosure
{
get
{
return this.Children.Select(c => c.Type)
.Concat(Children.SelectMany(c => c.ChildrenTransitiveClosure));
}
}
}
internal sealed class TypeTree
{
private static IEnumerable<Type> GetDerivedTypes(Type type)
{
// Across all assemblies
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
// Get all types in the assembly
foreach (var derivedType in assembly.GetTypes().Where(t => type.IsAssignableFrom(t) && t != type))
{
yield return derivedType;
}
}
}
private TypeNode EnsureTypeNode(Type type)
{
if (!this.TypeNodes.TryGetValue(type, out TypeNode? currentNode))
{
currentNode = this.TypeNodes[type] = new TypeNode(type);
}
return currentNode;
}
private void EnsureType(Type type)
{
TypeNode? currentNode = this.EnsureTypeNode(type);
while (currentNode != null &&
currentNode.Parent == null &&
!this.RootTypes.Contains(currentNode.Type))
{
Type parentType = currentNode.Type.BaseType
?? throw new InvalidOperationException("We should never have a non-Root, underived base");
TypeNode parentNode = this.EnsureTypeNode(parentType);
currentNode.Parent = parentNode;
parentNode.Children.Add(currentNode);
currentNode = parentNode;
}
}
public HashSet<Type> RootTypes { get; }
public Dictionary<Type, TypeNode> TypeNodes { get; } = new Dictionary<Type, TypeNode>();
public TypeTree(params Type[] rootTypes)
{
this.RootTypes = new HashSet<Type>();
foreach (var rootType in rootTypes)
{
// Check that there are no other types that this type derives from in the root types
// or vice versa
if (this.RootTypes.Any(t => t.IsAssignableFrom(rootType) || rootType.IsAssignableFrom(t)))
{
throw new ArgumentException($"Root types cannot be derived from each other: {rootType.Name}");
}
this.RootTypes.Add(rootType);
this.EnsureType(rootType);
foreach (var derivedType in GetDerivedTypes(rootType))
{
this.EnsureType(derivedType);
}
}
}
}
internal static readonly TypeTree MessageTypeTree = new(typeof(AgentMessage), typeof(GroupChatEventBase));
internal sealed class MessagesTypeInfoResolver : DefaultJsonTypeInfoResolver
{
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo baseTypeInfo = base.GetTypeInfo(type, options);
if (MessageTypeTree.TypeNodes.TryGetValue(type, out TypeNode? typeNode) &&
typeNode.Children.Any()) // Only add polymorphism info if there are derived children
{
if (baseTypeInfo.PolymorphismOptions == null)
{
baseTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions();
}
baseTypeInfo.PolymorphismOptions.IgnoreUnrecognizedTypeDiscriminators = true;
baseTypeInfo.PolymorphismOptions.UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization;
foreach (Type childType in typeNode.ChildrenTransitiveClosure)
{
if (childType.IsAbstract || childType.IsInterface || childType.IsGenericTypeDefinition)
{
// Can only deserialize concrete, complete types.
continue;
}
baseTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(childType, childType.FullName ?? childType.Name));
}
}
return baseTypeInfo;
}
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ModelContext.cs
using System.Text.Json;
using Microsoft.AutoGen.AgentChat.State;
using Microsoft.AutoGen.Contracts;
using LLMMessage = Microsoft.Extensions.AI.ChatMessage;
namespace Microsoft.AutoGen.AgentChat.Abstractions;
public interface IModelContext : ISaveState
{
public void Add(LLMMessage message);
public void Clear();
public IEnumerable<LLMMessage> Messages { get; }
}
public sealed class ModelContextState : BaseState
{
public List<LLMMessage> Messages { get; set; } = new();
}
public abstract class ModelContextBase : IModelContext
{
protected readonly List<LLMMessage> messages;
public abstract IEnumerable<LLMMessage> Messages { get; }
public ModelContextBase(params IEnumerable<LLMMessage> messages)
{
this.messages = [.. messages];
}
public void Add(LLMMessage message)
{
this.messages.Add(message);
}
public void Clear()
{
this.messages.Clear();
}
public ValueTask<JsonElement> SaveStateAsync()
{
SerializedState state = SerializedState.Create(new ModelContextState { Messages = this.messages });
return ValueTask.FromResult(state.AsJson());
}
public ValueTask LoadStateAsync(JsonElement state)
{
SerializedState serializedState = new(state);
ModelContextState modelContextState = serializedState.As<ModelContextState>();
this.messages.Clear();
this.messages.AddRange(modelContextState.Messages);
return ValueTask.CompletedTask;
}
}
public sealed class UnboundedModelContext : ModelContextBase
{
public UnboundedModelContext(params IEnumerable<LLMMessage> messages) : base(messages)
{
}
public override IEnumerable<LLMMessage> Messages => this.messages;
}
// TODO: Promote ModelContext to AutoGen.Core

View File

@ -3,6 +3,29 @@
namespace Microsoft.AutoGen.AgentChat.Abstractions;
public static class TerminationConditionExtensions
{
/// <summary>
/// Combine this termination condition with another using a logical OR.
/// </summary>
/// <param name="other">Another termination condition.</param>
/// <returns>The combined termination condition, with appropriate short-circuiting.</returns>
public static ITerminationCondition Or(this ITerminationCondition this_, ITerminationCondition other)
{
return new CombinerCondition(CombinerCondition.Or, this_, other);
}
/// <summary>
/// Combine this termination condition with another using a logical AND.
/// </summary>
/// <param name="other">Another termination condition.</param>
/// <returns>The combined termination condition, with appropriate short-circuiting.</returns>
public static ITerminationCondition And(this ITerminationCondition this_, ITerminationCondition other)
{
return new CombinerCondition(CombinerCondition.And, this_, other);
}
}
/// <summary>
/// A stateful condition that determines when a conversation should be terminated.
///
@ -12,7 +35,8 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions;
///
/// Once a termination condition has been reached, it must be <see cref="Reset()"/> before it can be used again.
///
/// Termination conditions can be combined using the <see cref="Or"/> and <see cref="And"/> methods.
/// Termination conditions can be combined using the <see cref="TerminationConditionExtensions.Or"/> and
/// <see cref="TerminationConditionExtensions.And"/> methods.
/// </summary>
public interface ITerminationCondition
{
@ -38,23 +62,37 @@ public interface ITerminationCondition
public void Reset();
/// <summary>
/// Combine this termination condition with another using a logical OR.
/// Combine two termination conditions with another using an associative, short-circuiting OR.
/// </summary>
/// <param name="other">Another termination condition.</param>
/// <returns>The combined termination condition, with appropriate short-circuiting.</returns>
public ITerminationCondition Or(ITerminationCondition other)
/// <param name="left">
/// The left-hand side termination condition. If this condition is already a disjunction, the RHS condition is added to the list of clauses.
/// </param>
/// <param name="right">
/// The right-hand side termination condition. If the LHS condition is already a disjunction, this condition is added to the list of clauses.
/// </param>
/// <returns>
/// The combined termination condition, with appropriate short-circuiting.
/// </returns>
public static ITerminationCondition operator |(ITerminationCondition left, ITerminationCondition right)
{
return new CombinerCondition(CombinerCondition.Or, this, other);
return left.Or(right);
}
/// <summary>
/// Combine this termination condition with another using a logical AND.
/// Combine two termination conditions with another using an associative, short-circuiting AND.
/// </summary>
/// <param name="other">Another termination condition.</param>
/// <returns>The combined termination condition, with appropriate short-circuiting.</returns>
public ITerminationCondition And(ITerminationCondition other)
/// <param name="left">
/// The left-hand side termination condition. If this condition is already a conjunction, the RHS condition is added to the list of clauses.
/// </param>
/// <param name="right">
/// The right-hand side termination condition. If the LHS condition is already a conjunction, this condition is added to the list of clauses.
/// </param>
/// <returns>
/// The combined termination condition, with appropriate short-circuiting.
/// </returns>
public static ITerminationCondition operator &(ITerminationCondition left, ITerminationCondition right)
{
return new CombinerCondition(CombinerCondition.And, this, other);
return left.And(right);
}
}
@ -167,38 +205,4 @@ internal sealed class CombinerCondition : ITerminationCondition
return null;
}
/// <inheritdoc cref="ITerminationCondition.Or" />
/// <remarks>
/// If this condition is already a disjunction, the new condition is added to the list of clauses.
/// </remarks>
ITerminationCondition ITerminationCondition.Or(ITerminationCondition other)
{
if (this.conjunction == Or)
{
this.clauses.Add(other);
return this;
}
else
{
return new CombinerCondition(Or, this, new CombinerCondition(Or, other));
}
}
/// <inheritdoc cref="ITerminationCondition.And" />
/// <remarks>
/// If this condition is already a conjunction, the new condition is added to the list of clauses.
/// </remarks>
ITerminationCondition ITerminationCondition.And(ITerminationCondition other)
{
if (this.conjunction == And)
{
this.clauses.Add(other);
return this;
}
else
{
return new CombinerCondition(And, this, new CombinerCondition(And, other));
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Tools.cs
using System.ComponentModel;
using System.Reflection;
using Microsoft.Extensions.AI;
@ -10,37 +9,6 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions;
// TODO: This likely should live as a "Component" in an Agent-building ClassLib?
// It seems like it could have applicability beyond AgentChat.
public static class ReflectionExtensions
{
public static AIFunctionParameterMetadata ToAIFunctionMetadata(this ParameterInfo pi)
{
return new AIFunctionParameterMetadata(pi.Name!)
{
Description = pi.GetCustomAttribute<DescriptionAttribute>()?.Description,
ParameterType = pi.ParameterType,
HasDefaultValue = pi.HasDefaultValue,
IsRequired = !pi.HasDefaultValue,
DefaultValue = pi.DefaultValue,
// Schema = JSONSchema of type
};
}
public static AIFunctionReturnParameterMetadata ToAIFunctionReturnMetadata(this ParameterInfo rpi)
{
return new AIFunctionReturnParameterMetadata
{
Description = rpi.GetCustomAttribute<DescriptionAttribute>()?.Description,
ParameterType = rpi.ParameterType
//Schema = JSONSchema of type
};
}
}
public class ParameterSchema(string name, Type type, bool isRequired = false, object? defaultValue = default)
{
public string Name { get; } = name;
@ -54,15 +22,6 @@ public class ParameterSchema(string name, Type type, bool isRequired = false, ob
Type parameterType = parameterInfo.ParameterType;
return ParameterSchema<object>.Create(parameterType, parameterInfo.Name!, parameterInfo.HasDefaultValue, parameterInfo.DefaultValue);
}
public static implicit operator ParameterSchema(AIFunctionParameterMetadata parameterMetadata)
{
Type parameterType = parameterMetadata.ParameterType!; // TODO: Deal with missing ParameterTypes
return ParameterSchema<object>.Create(parameterType,
parameterMetadata.Name,
parameterMetadata.IsRequired,
parameterMetadata.DefaultValue);
}
}
// TODO: Can this be obviated by AIFunctionParameter?
@ -86,7 +45,6 @@ public interface ITool
public string Description { get; }
public IEnumerable<ParameterSchema> Parameters { get; }
public Type ReturnType { get; }
// TODO: State serialization
@ -136,18 +94,15 @@ public class AIFunctionTool(AIFunction aiFunction) : ITool
public AIFunction AIFunction { get; } = aiFunction;
/// <inheritdoc cref="ITool.Name" />
public string Name => this.AIFunction.Metadata.Name;
public string Name => this.AIFunction.Name;
/// <inheritdoc cref="ITool.Description" />
public string Description => this.AIFunction.Metadata.Description;
public string Description => this.AIFunction.Description;
/// <inheritdoc cref="ITool.Parameters" />
public IEnumerable<ParameterSchema> Parameters => from rawParameter in this.AIFunction.Metadata.Parameters
public IEnumerable<ParameterSchema> Parameters => from rawParameter in this.AIFunction.UnderlyingMethod!.GetParameters()
select (ParameterSchema)rawParameter;
/// <inheritdoc cref="ITool.ReturnType" />
public Type ReturnType => this.AIFunction.Metadata.ReturnParameter.ParameterType!; // TODO: Deal with missing return types
/// <inheritdoc cref="ITool.ExecuteAsync" />
public Task<object> ExecuteAsync(IEnumerable<object> parameters, CancellationToken cancellationToken = default)
=> this.ExecuteAsync(parameters, cancellationToken);
@ -164,23 +119,6 @@ public class CallableTool(string name, string description, Delegate callable)
{
internal static AIFunction CreateAIFunction(string name, string description, Delegate callable)
{
MethodInfo methodInfo = callable.Method;
IEnumerable<AIFunctionParameterMetadata> parameters =
from parameterInfo in methodInfo.GetParameters()
select parameterInfo.ToAIFunctionMetadata();
AIFunctionReturnParameterMetadata returnParameter = methodInfo.ReturnParameter.ToAIFunctionReturnMetadata();
AIFunctionFactoryCreateOptions createOptions = new()
{
Name = name,
Description = description,
Parameters = parameters.ToList(),
ReturnParameter = returnParameter,
// SerializerOptions = TODO: How do we maintain consistency with Python?
};
return AIFunctionFactory.Create(callable, createOptions);
return AIFunctionFactory.Create(callable, name: name, description: description);
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Usage.cs
namespace Microsoft.AutoGen.AgentChat.Abstractions;
public struct RequestUsage
{
public int PromptTokens { get; set; }
public int CompletionTokens { get; set; }
}

View File

@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ChatAgentRouter.cs
using System.Text.Json;
using Microsoft.AutoGen.AgentChat.Abstractions;
using Microsoft.AutoGen.AgentChat.State;
using Microsoft.AutoGen.Contracts;
using Microsoft.AutoGen.Core;
using Microsoft.Extensions.Logging;
@ -24,7 +26,8 @@ internal sealed class ChatAgentRouter : HostableAgentAdapter,
IHandle<GroupChatStart>,
IHandle<GroupChatAgentResponse>,
IHandle<GroupChatRequestPublish>,
IHandle<GroupChatReset>
IHandle<GroupChatReset>,
ISaveState
{
private readonly TopicId parentTopic;
private readonly TopicId outputTopic;
@ -66,7 +69,6 @@ internal sealed class ChatAgentRouter : HostableAgentAdapter,
// place.
await foreach (ChatStreamFrame frame in this.agent.StreamAsync(this.MessageBuffer, messageContext.CancellationToken))
{
// TODO: call publish message
switch (frame.Type)
{
case ChatStreamFrame.FrameType.Response:
@ -94,5 +96,24 @@ internal sealed class ChatAgentRouter : HostableAgentAdapter,
this.MessageBuffer.Clear();
return this.agent.ResetAsync(messageContext.CancellationToken);
}
async ValueTask<JsonElement> ISaveState.SaveStateAsync()
{
ChatAgentContainerState state = new ChatAgentContainerState
{
AgentState = new SerializedState(await this.agent.SaveStateAsync()),
MessageBuffer = this.MessageBuffer
};
return SerializedState.Create(state).AsJson();
}
ValueTask ISaveState.LoadStateAsync(JsonElement state)
{
ChatAgentContainerState parsedState = new SerializedState(state).As<ChatAgentContainerState>();
this.MessageBuffer = parsedState.MessageBuffer;
return this.agent.LoadStateAsync(parsedState.AgentState.AsJson());
}
}

View File

@ -4,7 +4,9 @@
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.AutoGen.AgentChat.Abstractions;
using Microsoft.AutoGen.AgentChat.State;
using Microsoft.AutoGen.Contracts;
using Microsoft.AutoGen.Core;
@ -78,7 +80,8 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
private GroupChatOptions GroupChatOptions { get; }
private readonly List<AgentMessage> messageThread = new();
private readonly RuntimeLayer runtimeLayer;
private Dictionary<string, AgentChatConfig> Participants { get; } = new();
protected GroupChatBase(List<IChatAgent> participants, ITerminationCondition? terminationCondition = null, int? maxTurns = null)
@ -96,9 +99,10 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
this.GroupChatOptions.Participants[participant.Name] = new GroupParticipant(config.ParticipantTopicType, participant.Description);
}
this.messageThread = new List<AgentMessage>(); // TODO: Allow injecting this
this.TeamId = Guid.NewGuid().ToString().ToLowerInvariant();
this.runtimeLayer = new RuntimeLayer(this);
this.RunManager = new(this.InitializationLayersInternal);
}
public string TeamId
@ -128,17 +132,58 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
throw new Exception("Could not create chat manager; make sure that it contains a ctor() or ctor(GroupChatOptions), or override the CreateChatManager method");
}
// TODO: Turn this into an IDisposable-based utility
private int running; // = 0
private bool EnsureSingleRun()
private sealed class RuntimeLayer(GroupChatBase<TManager> groupChat) : IRunContextLayer
{
return Interlocked.CompareExchange(ref running, 1, 0) == 0;
public GroupChatBase<TManager> GroupChat { get; } = groupChat;
public InProcessRuntime? Runtime { get; private set; }
public OutputSink? OutputSink { get; private set; }
public Task? InitOnceTask { get; set; }
public Task ShutdownTask { get; set; } = Task.CompletedTask;
public async ValueTask DeinitializeAsync()
{
await this.ShutdownTask;
}
private async Task CreateRuntime()
{
this.Runtime = new InProcessRuntime();
foreach (AgentChatConfig config in this.GroupChat.Participants.Values)
{
await this.Runtime.RegisterChatAgentAsync(config);
}
await this.Runtime.RegisterGroupChatManagerAsync(this.GroupChat.GroupChatOptions, this.GroupChat.TeamId, this.GroupChat.CreateChatManager);
this.OutputSink = new OutputSink();
await this.Runtime.RegisterOutputCollectorAsync(this.OutputSink, this.GroupChat.GroupChatOptions.OutputTopicType);
}
public bool HasRunOnce => this.InitOnceTask != null;
public async ValueTask InitializeAsync()
{
if (this.InitOnceTask == null)
{
this.InitOnceTask = this.CreateRuntime();
}
await this.InitOnceTask;
await this.Runtime!.StartAsync();
}
}
private void EndRun()
{
this.running = 0;
}
private IRunContextLayer[] InitializationLayersInternal =>
[
this.runtimeLayer, ..this.InitializationLayers
];
protected virtual IEnumerable<IRunContextLayer> InitializationLayers => [];
private RunManager RunManager { get; }
public IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken)
{
@ -157,79 +202,149 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
return this.StreamAsync(taskStart, cancellationToken);
}
public ValueTask ResetAsync(CancellationToken cancel)
private InProcessRuntime? Runtime => this.runtimeLayer.Runtime;
private OutputSink? OutputSink => this.runtimeLayer.OutputSink;
private Task ShutdownTask
{
return ValueTask.CompletedTask;
get => this.runtimeLayer.ShutdownTask;
set => this.runtimeLayer.ShutdownTask = value;
}
public async IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, [EnumeratorCancellation] CancellationToken cancellationToken = default)
private Func<CancellationToken, ValueTask> PrepareStream(ChatMessage task)
{
GroupChatStart taskMessage = new GroupChatStart
{
Messages = [task]
};
return async (CancellationToken cancellationToken) =>
{
AgentId chatManagerId = new AgentId(GroupChatManagerTopicType, this.TeamId);
await this.Runtime!.SendMessageAsync(taskMessage, chatManagerId, cancellationToken: cancellationToken);
this.ShutdownTask = Task.Run(this.Runtime!.RunUntilIdleAsync);
};
}
private async IAsyncEnumerable<TaskFrame> StreamOutput([EnumeratorCancellation] CancellationToken cancellationToken)
{
List<AgentMessage> runMessages = new();
while (true)
{
OutputSink.SinkFrame frame = await this.OutputSink!.WaitForDataAsync(cancellationToken);
runMessages.AddRange(frame.Messages);
foreach (AgentMessage message in frame.Messages)
{
yield return new TaskFrame(message);
}
if (frame.IsTerminal)
{
TaskResult result = new TaskResult(runMessages);
yield return new TaskFrame(result);
break;
}
}
}
public IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, CancellationToken cancellationToken = default)
{
if (task == null)
{
throw new ArgumentNullException(nameof(task));
}
if (!this.EnsureSingleRun())
{
throw new InvalidOperationException("The task is already running.");
}
// TODO: How do we allow the user to configure this?
//AgentsAppBuilder builder = new AgentsAppBuilder().UseInProcessRuntime();
InProcessRuntime runtime = new InProcessRuntime();
foreach (AgentChatConfig config in this.Participants.Values)
{
await runtime.RegisterChatAgentAsync(config);
}
await runtime.RegisterGroupChatManagerAsync(this.GroupChatOptions, this.TeamId, this.CreateChatManager);
OutputSink outputSink = new OutputSink();
await runtime.RegisterOutputCollectorAsync(outputSink, this.GroupChatOptions.OutputTopicType);
await runtime.StartAsync();
Task shutdownTask = Task.CompletedTask;
const string TaskAlreadyRunning = "The task is already running";
return this.RunManager.StreamAsync(
this.StreamOutput,
cancellationToken,
this.PrepareStream(task),
TaskAlreadyRunning);
}
private async ValueTask ResetInternalAsync(CancellationToken cancel)
{
try
{
// TODO: Protos
GroupChatStart taskMessage = new GroupChatStart
foreach (var participant in this.Participants.Values)
{
Messages = [task]
};
List<AgentMessage> runMessages = new();
AgentId chatManagerId = new AgentId(GroupChatManagerTopicType, this.TeamId);
await runtime.SendMessageAsync(taskMessage, chatManagerId, cancellationToken: cancellationToken);
shutdownTask = Task.Run(runtime.RunUntilIdleAsync);
while (true)
{
OutputSink.SinkFrame frame = await outputSink.WaitForDataAsync(cancellationToken);
runMessages.AddRange(frame.Messages);
foreach (AgentMessage message in frame.Messages)
{
yield return new TaskFrame(message);
}
if (frame.IsTerminal)
{
TaskResult result = new TaskResult(runMessages);
yield return new TaskFrame(result);
break;
}
await this.Runtime!.SendMessageAsync(
new GroupChatReset(),
new AgentId(participant.ParticipantTopicType, this.TeamId),
cancellationToken: cancel);
}
await this.Runtime!.SendMessageAsync(
new GroupChatReset(),
new AgentId(GroupChatManagerTopicType, this.TeamId),
cancellationToken: cancel);
await this.Runtime!.RunUntilIdleAsync();
}
finally
{
this.EndRun();
this.OutputSink?.Reset();
}
}
await shutdownTask;
public ValueTask ResetAsync(CancellationToken cancel)
{
const string TaskAlreadyRunning = "The group chat is currently running. It must be stopped before it can be reset.";
return this.RunManager.RunAsync(
this.ResetInternalAsync,
cancel,
message: TaskAlreadyRunning);
}
public ValueTask<JsonElement> SaveStateAsync()
{
if (!this.runtimeLayer.HasRunOnce)
{
throw new InvalidOperationException("The group chat has not been initialized. It must be run before it can be saved.");
}
const string TaskAlreadyRunning = "The team cannot be saved while it is running.";
return this.RunManager.RunAsync(
SaveStateInternalAsync,
CancellationToken.None, // TODO: Change this API?
message: TaskAlreadyRunning);
async ValueTask<JsonElement> SaveStateInternalAsync(CancellationToken _)
{
TeamState teamState = new()
{
TeamId = this.TeamId,
RuntimeState = await this.Runtime!.SaveStateAsync(),
};
JsonElement result = SerializedState.Create(teamState).AsJson();
await this.Runtime!.StopAsync();
return result;
}
}
public ValueTask LoadStateAsync(JsonElement state)
{
const string TaskAlreadyRunning = "The team cannot be loaded while it is running.";
return this.RunManager.RunAsync(
LoadStateInternalAsync,
CancellationToken.None, // TODO: Change this API?
message: TaskAlreadyRunning);
async ValueTask LoadStateInternalAsync(CancellationToken _)
{
TeamState parsedState = new SerializedState(state).As<TeamState>();
this.TeamId = parsedState.TeamId;
await this.Runtime!.LoadStateAsync(parsedState.RuntimeState.AsJson());
await this.Runtime!.StopAsync();
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// GroupChatHandlerRouter.cs
using System.Text.Json;
using Microsoft.AutoGen.Contracts;
using Microsoft.AutoGen.Core;
using Microsoft.Extensions.Logging;
@ -9,7 +10,7 @@ namespace Microsoft.AutoGen.AgentChat.GroupChat;
internal delegate ValueTask MessagePublishServicer(GroupChatEventBase event_, string topicType, CancellationToken cancellation = default);
internal interface IGroupChatHandler : IHandle<GroupChatStart>, IHandle<GroupChatAgentResponse>, IHandle<object>
internal interface IGroupChatHandler : IHandle<GroupChatStart>, IHandle<GroupChatAgentResponse>, IHandle<object>, ISaveState
{
public void AttachMessagePublishServicer(MessagePublishServicer? servicer = null);
public void DetachMessagePublishServicer() => this.AttachMessagePublishServicer(null);
@ -18,7 +19,8 @@ internal interface IGroupChatHandler : IHandle<GroupChatStart>, IHandle<GroupCha
internal sealed class GroupChatHandlerRouter<TManager> : HostableAgentAdapter,
IHandle<GroupChatStart>,
IHandle<GroupChatAgentResponse>,
IHandle<object>
IHandle<object>,
ISaveState
where TManager : GroupChatManagerBase, IGroupChatHandler
{
@ -45,4 +47,10 @@ internal sealed class GroupChatHandlerRouter<TManager> : HostableAgentAdapter,
public ValueTask HandleAsync(object item, MessageContext messageContext)
=> this.ChatManager.HandleAsync(item, messageContext);
ValueTask<JsonElement> ISaveState.SaveStateAsync()
=> this.ChatManager.SaveStateAsync();
ValueTask ISaveState.LoadStateAsync(JsonElement state)
=> this.ChatManager.LoadStateAsync(state);
}

View File

@ -33,7 +33,7 @@ public abstract class GroupChatManagerBase : IGroupChatHandler
protected ITerminationCondition? TerminationCondition => this.options.TerminationCondition;
protected int? MaxTurns => this.options.MaxTurns;
private int CurrentTurn { get; set; }
protected int CurrentTurn { get; set; }
protected List<AgentMessage> MessageThread;
@ -182,6 +182,6 @@ public abstract class GroupChatManagerBase : IGroupChatHandler
public ValueTask HandleAsync(object item, MessageContext messageContext)
{
throw new InvalidOperationException($"Unhandled message in group chat manager: {item.GetType()}");
throw new NotImplementedException();
}
}

View File

@ -26,7 +26,7 @@ internal sealed class OutputSink : IOutputCollectionSink
}
private readonly object sync = new();
private SemaphoreSlim semapohre = new SemaphoreSlim(1, 1);
private SemaphoreSlim semapohre = new SemaphoreSlim(0, 1);
private SinkFrame? receivingSinkFrame;
@ -43,7 +43,12 @@ internal sealed class OutputSink : IOutputCollectionSink
frameAction(this.receivingSinkFrame);
}
semapohre.Release();
// TODO: Replace the Semaphore with a TaskSource approach
try
{
semapohre.Release();
}
catch (SemaphoreFullException) { }
}
public void CollectMessage(AgentMessage message)
@ -82,6 +87,14 @@ internal sealed class OutputSink : IOutputCollectionSink
await this.semapohre.WaitAsync(cancellation);
}
}
internal void Reset()
{
lock (this.sync)
{
this.receivingSinkFrame = null;
}
}
}
// TODO: Abstract the core logic of this out into the equivalent of ClosureAgent, because that seems like a

View File

@ -1,14 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// RoundRobinGroupChat.cs
using System.Text.Json;
using Microsoft.AutoGen.AgentChat.Abstractions;
using Microsoft.AutoGen.AgentChat.State;
using Microsoft.AutoGen.Contracts;
namespace Microsoft.AutoGen.AgentChat.GroupChat;
/// <summary>
/// A group chat manager that selects the next speaker in a round-robin fashion.
/// </summary>
public class RoundRobinGroupChatManager : GroupChatManagerBase
public class RoundRobinGroupChatManager : GroupChatManagerBase, ISaveState
{
private readonly List<string> participantTopicTypes;
private int nextSpeakerIndex;
@ -28,6 +31,29 @@ public class RoundRobinGroupChatManager : GroupChatManagerBase
return ValueTask.FromResult(result);
}
ValueTask<JsonElement> ISaveState.SaveStateAsync()
{
RoundRobinManagerState state = new RoundRobinManagerState
{
NextSpeakerIndex = this.nextSpeakerIndex,
CurrentTurn = this.CurrentTurn,
MessageThread = this.MessageThread
};
return ValueTask.FromResult(SerializedState.Create(state).AsJson());
}
ValueTask ISaveState.LoadStateAsync(JsonElement state)
{
RoundRobinManagerState parsedState = new SerializedState(state).As<RoundRobinManagerState>();
this.MessageThread = parsedState.MessageThread;
this.CurrentTurn = parsedState.CurrentTurn;
this.nextSpeakerIndex = parsedState.NextSpeakerIndex;
return ValueTask.CompletedTask;
}
}
/// <summary>

View File

@ -0,0 +1,297 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// RunContext.cs
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Microsoft.AutoGen.AgentChat.GroupChat;
public abstract class LifecycleObject
{
private int initialized;
private void PrepareInitialize(Action errorAction)
{
if (Interlocked.CompareExchange(ref this.initialized, 1, 0) != 0)
{
errorAction();
}
}
private void PrepareDeinitialize(Action errorAction)
{
if (Interlocked.CompareExchange(ref this.initialized, 0, 1) != 1)
{
errorAction();
}
}
protected bool IsInitialized => Volatile.Read(ref this.initialized) == 1;
protected virtual void OnInitializeError() => throw new InvalidOperationException($"Error initializing: {this.GetType().FullName}; already initialized.");
protected virtual void OnDeinitializeError() => throw new InvalidOperationException($"Error deinitializing: {this.GetType().FullName}; not initialized.");
public ValueTask InitializeAsync()
{
this.PrepareInitialize(this.OnInitializeError);
return this.InitializeCore();
}
public ValueTask DeinitializeAsync()
{
this.PrepareDeinitialize(this.OnDeinitializeError);
return this.DeinitializeCore();
}
protected abstract ValueTask InitializeCore();
protected abstract ValueTask DeinitializeCore();
}
public interface IRunContextLayer
{
public ValueTask InitializeAsync();
public ValueTask DeinitializeAsync();
}
public sealed class RunContextStack : LifecycleObject, IRunContextLayer
{
private Stack<IRunContextLayer> Uninitialized { get; } = new();
private Stack<IRunContextLayer> Initialized { get; } = new();
public RunContextStack(params IEnumerable<IRunContextLayer> contextLayers)
{
this.Uninitialized = new Stack<IRunContextLayer>(contextLayers);
}
// TODO: There is probably a way to have a sound manner by which pushing/popping a layer when initialized
// would be allowed. But this is not necessary for now, so we will keep it simple.
public void PushLayer(IRunContextLayer layer)
{
if (this.IsInitialized)
{
throw new InvalidOperationException("Cannot push a layer while the context is initialized.");
}
this.Uninitialized.Push(layer);
}
public void PopLayer()
{
if (this.IsInitialized)
{
throw new InvalidOperationException("Cannot pop a layer while the context is initialized.");
}
}
private Action? initializeError;
protected override void OnInitializeError()
{
(this.initializeError ?? base.OnInitializeError)();
}
private Action? deinitializeError;
protected override void OnDeinitializeError()
{
(this.deinitializeError ?? base.OnDeinitializeError)();
}
public static IRunContextLayer OverrideErrors(Action? initializeError = null, Action? deinitializeError = null)
{
return new ErrorOverrideLayer(initializeError, deinitializeError);
}
private sealed class ErrorOverrideLayer(Action? initializeError = null, Action? deinitializeError = null)
: IRunContextLayer
{
public RunContextStack? Target { get; set; }
private Action? initializeErrorPrev;
private Action? deinitializeErrorPrev;
public ValueTask InitializeAsync()
{
if (initializeError != null)
{
this.initializeErrorPrev = Interlocked.CompareExchange(ref this.Target!.initializeError, initializeError, null);
}
if (deinitializeError != null)
{
this.deinitializeErrorPrev = Interlocked.CompareExchange(ref this.Target!.deinitializeError, deinitializeError, null);
}
return ValueTask.CompletedTask;
}
public ValueTask DeinitializeAsync()
{
if (this.initializeErrorPrev != null)
{
Interlocked.CompareExchange(ref this.Target!.initializeError, this.initializeErrorPrev, initializeError);
}
if (this.deinitializeErrorPrev != null)
{
Interlocked.CompareExchange(ref this.Target!.deinitializeError, this.deinitializeErrorPrev, deinitializeError);
}
return ValueTask.CompletedTask;
}
}
public ValueTask<IAsyncDisposable> Enter()
{
return RunTicket.Enter(this);
}
protected override async ValueTask InitializeCore()
{
while (this.Uninitialized.Count > 0)
{
IRunContextLayer layer = this.Uninitialized.Pop();
if (layer is ErrorOverrideLayer errorOverrideLayer)
{
errorOverrideLayer.Target = this;
}
await layer.InitializeAsync();
this.Initialized.Push(layer);
}
}
protected override async ValueTask DeinitializeCore()
{
while (this.Initialized.Count > 0)
{
IRunContextLayer layer = this.Initialized.Pop();
await layer.DeinitializeAsync();
this.Uninitialized.Push(layer);
}
}
private sealed class RunTicket : IAsyncDisposable
{
private RunContextStack contextStack;
private int disposed;
private RunTicket(RunContextStack contextStack)
{
Debug.Assert(contextStack.IsInitialized, "The context stack must be initialized.");
this.contextStack = contextStack;
}
public static async ValueTask<IAsyncDisposable> Enter(RunContextStack contextStack)
{
await contextStack.InitializeAsync();
return new RunTicket(contextStack);
}
public ValueTask DisposeAsync()
{
if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 0)
{
return this.contextStack.DeinitializeAsync();
}
return ValueTask.CompletedTask;
}
}
}
public sealed class RunManager
{
private RunContextStack runContextStack;
public RunManager(params IEnumerable<IRunContextLayer> contextLayers)
{
this.runContextStack = new RunContextStack(contextLayers);
}
private ValueTask<IAsyncDisposable> PrepareRunAsync(string? message = null)
{
if (message != null)
{
IRunContextLayer errorOverride = RunContextStack.OverrideErrors(() => throw new InvalidOperationException(message));
this.runContextStack.PushLayer(errorOverride);
}
return this.runContextStack.Enter();
}
private async ValueTask EndRunAsync(IAsyncDisposable? runDisposable, bool hadMessage)
{
if (runDisposable != null)
{
await runDisposable.DisposeAsync().ConfigureAwait(false);
}
if (hadMessage)
{
this.runContextStack.PopLayer();
}
}
public async ValueTask RunAsync(Func<CancellationToken, ValueTask> asyncAction, CancellationToken cancellation = default, Func<CancellationToken, ValueTask>? prepareAction = null, string? message = null)
{
IAsyncDisposable? runDisposable = null;
try
{
runDisposable = await this.PrepareRunAsync(message).ConfigureAwait(false);
if (prepareAction != null)
{
await prepareAction(cancellation).ConfigureAwait(false);
}
await asyncAction(cancellation).ConfigureAwait(false);
}
finally
{
await this.EndRunAsync(runDisposable, message != null).ConfigureAwait(false);
}
}
public async ValueTask<T> RunAsync<T>(Func<CancellationToken, ValueTask<T>> asyncAction, CancellationToken cancellation = default, Func<CancellationToken, ValueTask>? prepareAction = null, string? message = null)
{
IAsyncDisposable? runDisposable = null;
try
{
runDisposable = await this.PrepareRunAsync(message).ConfigureAwait(false);
if (prepareAction != null)
{
await prepareAction(cancellation).ConfigureAwait(false);
}
return await asyncAction(cancellation).ConfigureAwait(false);
}
finally
{
await this.EndRunAsync(runDisposable, message != null).ConfigureAwait(false);
}
}
public async IAsyncEnumerable<TItem> StreamAsync<TItem>(Func<CancellationToken, IAsyncEnumerable<TItem>> streamAction, [EnumeratorCancellation] CancellationToken cancellation = default, Func<CancellationToken, ValueTask>? prepareAction = null, string? message = null)
{
IAsyncDisposable? runDisposable = null;
try
{
runDisposable = await this.PrepareRunAsync(message).ConfigureAwait(false);
if (prepareAction != null)
{
await prepareAction(cancellation).ConfigureAwait(false);
}
await foreach (TItem item in streamAction(cancellation))
{
yield return item;
}
}
finally
{
await this.EndRunAsync(runDisposable, message != null).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// BaseState.cs
namespace Microsoft.AutoGen.AgentChat.State;
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class StateSerializableAttribute : Attribute
{
public StateSerializableAttribute()
{
}
}
[StateSerializable]
public abstract class BaseState
{
public string Type => this.GetType().FullName!;
public string Version { get; set; } = "1.0.0"; // TODO: More rigorous state versioning?
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ChatAgentContainerState.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.State;
public class ChatAgentContainerState : BaseState
{
public required SerializedState AgentState { get; set; }
public List<ChatMessage> MessageBuffer { get; set; } = new();
}
public class GroupChatManagerStateBase : BaseState
{
public List<AgentMessage> MessageThread { get; set; } = new();
public int CurrentTurn { get; set; }
}
public class RoundRobinManagerState : GroupChatManagerStateBase
{
public int NextSpeakerIndex { get; set; }
}

View File

@ -0,0 +1,100 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SerializedState.cs
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.State;
public class SerializedStateConverter : JsonConverter<SerializedState>
{
public override SerializedState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var json = JsonDocument.ParseValue(ref reader).RootElement;
var state = new SerializedState(json);
return state;
}
public override void Write(Utf8JsonWriter writer, SerializedState value, JsonSerializerOptions options)
{
value.AsJson().WriteTo(writer);
}
}
[JsonConverter(typeof(SerializedStateConverter))]
public class SerializedState
{
private readonly JsonSerializerOptions SerializerOptions = new()
{
Converters =
{
new SerializedStateConverter(),
},
TypeInfoResolver = new MessageSerializationHelpers.MessagesTypeInfoResolver(),
};
public JsonElement AsJson()
{
if (this.jsonValue != null)
{
return this.jsonValue.Value;
}
if (this.deserializedValue == null)
{
throw new InvalidOperationException("State is not initialized.");
}
this.jsonValue = JsonSerializer.SerializeToElement(this.deserializedValue, SerializerOptions);
return this.jsonValue.Value;
}
public static SerializedState Create<T>(T state) where T : notnull
{
return new SerializedState((object)state);
}
public T As<T>()
{
if (this.deserializedValue is T value)
{
return value;
}
if (this.deserializedValue != null)
{
throw new InvalidOperationException($"Cannot convert state of type {this.deserializedValue.GetType()} to {typeof(T)}.");
}
if (this.jsonValue == null)
{
throw new InvalidOperationException("State is not initialized.");
}
T? result = JsonSerializer.Deserialize<T>(this.jsonValue!.Value, SerializerOptions)
?? throw new InvalidOperationException($"Cannot deserialize state to {typeof(T)}.");
this.deserializedValue = result;
return result;
}
private object? deserializedValue;
private JsonElement? jsonValue;
private SerializedState(object state)
{
this.deserializedValue = state;
}
public SerializedState(JsonElement json)
{
this.jsonValue = json;
}
public static implicit operator SerializedState(JsonElement json)
{
return new SerializedState(json);
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// TeamState.cs
namespace Microsoft.AutoGen.AgentChat.State;
public sealed class TeamState : BaseState
{
public required string TeamId { get; set; }
public required SerializedState RuntimeState { get; set; }
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ExternalTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// A <see cref="ITerminationCondition"/> that is externally controlled by calling the <see cref="Set"/> method.
/// </summary>
public sealed class ExternalTermination : ITerminationCondition
{
public ExternalTermination()
{
this.TerminationQueued = false;
this.IsTerminated = false;
}
public bool TerminationQueued { get; private set; }
public bool IsTerminated { get; private set; }
/// <summary>
/// Set the termination condition to terminated.
/// </summary>
public void Set()
{
this.TerminationQueued = true;
}
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
if (this.TerminationQueued)
{
this.IsTerminated = true;
string message = "External termination requested.";
StopMessage result = new() { Content = message, Source = nameof(ExternalTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.TerminationQueued = false;
this.IsTerminated = false;
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// FunctionCallTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation if a <see cref="ToolCallExecutionEvent"/> with a specific name is received.
/// </summary>
public sealed class FunctionCallTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="FunctionCallTermination"/> class.
/// </summary>
/// <param name="functionName">The name of the function to look for in the messages.</param>
public FunctionCallTermination(string functionName)
{
this.FunctionName = functionName;
this.IsTerminated = false;
}
public string FunctionName { get; }
public bool IsTerminated { get; private set; }
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
foreach (AgentMessage item in messages)
{
if (item is ToolCallExecutionEvent toolCallEvent && toolCallEvent.Content.Any(execution => execution.Name == this.FunctionName))
{
this.IsTerminated = true;
string message = $"Function '{this.FunctionName}' was executed.";
StopMessage result = new() { Content = message, Source = nameof(FunctionCallTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.IsTerminated = false;
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// HandoffTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation if a <see cref="HandoffMessage"/> with the given <see cref="HandoffMessage.Target"/>
/// is received.
/// </summary>
public sealed class HandoffTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="HandoffTermination"/> class.
/// </summary>
/// <param name="target">The target of the handoff message.</param>
public HandoffTermination(string target)
{
this.Target = target;
this.IsTerminated = false;
}
public string Target { get; }
public bool IsTerminated { get; private set; }
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
foreach (AgentMessage item in messages)
{
if (item is HandoffMessage handoffMessage && handoffMessage.Target == this.Target)
{
this.IsTerminated = true;
string message = $"Handoff to {handoffMessage.Target} from {handoffMessage.Source} detected.";
StopMessage result = new() { Content = message, Source = nameof(HandoffTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.IsTerminated = false;
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// MaxMessageTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation after a maximum number of messages have been exchanged.
/// </summary>
public sealed class MaxMessageTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="MaxMessageTermination"/> class.
/// </summary>
/// <param name="maxMessages">The maximum number of messages allowed in the conversation.</param>
public MaxMessageTermination(int maxMessages, bool includeAgentEvent = false)
{
this.MaxMessages = maxMessages;
this.MessageCount = 0;
this.IncludeAgentEvent = includeAgentEvent;
}
public int MaxMessages { get; }
public int MessageCount { get; private set; }
public bool IncludeAgentEvent { get; }
public bool IsTerminated => this.MessageCount >= this.MaxMessages;
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
this.MessageCount += messages.Where(m => this.IncludeAgentEvent || m is not AgentEvent).Count();
if (this.IsTerminated)
{
StopMessage result = new() { Content = "Max message count reached", Source = nameof(MaxMessageTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.MessageCount = 0;
}
}

View File

@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SourceMatchTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation after a specific source responds.
/// </summary>
public sealed class SourceMatchTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="SourceMatchTermination"/> class.
/// </summary>
/// <param name="sources">List of source names to terminate the conversation.</param>
public SourceMatchTermination(params IEnumerable<string> sources)
{
this.Sources = [.. sources];
}
public HashSet<string> Sources { get; }
public bool IsTerminated { get; private set; }
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
foreach (AgentMessage item in messages)
{
if (this.Sources.Contains(item.Source))
{
this.IsTerminated = true;
string message = $"'{item.Source}' answered.";
StopMessage result = new() { Content = message, Source = nameof(SourceMatchTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.IsTerminated = false;
}
}

View File

@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// TextMentionTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
using Microsoft.Extensions.AI;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation if a specific text is mentioned.
/// </summary>
public sealed class TextMentionTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="TextMentionTermination"/> class.
/// </summary>
/// <param name="targetText">The text to look for in the messages.</param>
/// <param name="sources">Check only the messages of the specified agents for the text to look for.</param>
public TextMentionTermination(string targetText, IEnumerable<string>? sources = null)
{
this.TargetText = targetText;
this.Sources = sources != null ? [.. sources] : null;
this.IsTerminated = false;
}
public string TargetText { get; }
public HashSet<string>? Sources { get; }
public bool IsTerminated { get; private set; }
private bool CheckMultiModalData(MultiModalData data)
{
return data.ContentType == MultiModalData.Type.String &&
((TextContent)data.AIContent).Text.Contains(this.TargetText);
}
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
foreach (AgentMessage item in messages)
{
if (this.Sources != null && !this.Sources.Contains(item.Source))
{
continue;
}
bool hasMentions = item switch
{
TextMessage textMessage => textMessage.Content.Contains(this.TargetText),
MultiModalMessage multiModalMessage => multiModalMessage.Content.Any(CheckMultiModalData),
StopMessage stopMessage => stopMessage.Content.Contains(this.TargetText),
ToolCallSummaryMessage toolCallSummaryMessage => toolCallSummaryMessage.Content.Contains(this.TargetText),
_ => false
};
if (hasMentions)
{
this.IsTerminated = true;
StopMessage result = new() { Content = "Text mention received", Source = nameof(TextMentionTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.IsTerminated = false;
}
}

View File

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// TextMessageTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation if a <see cref="TextMessage"/> is received.
///
/// This termination condition checks for TextMessage instances in the message sequence. When a TextMessage is found,
/// it terminates the conversation if either:
///
/// <list type="bullet">
/// <item>No source was specified (terminates on any <see cref="TextMessage"/>)</item>
/// <item>The message source matches the specified source</item>
/// </list>
///
/// </summary>
public sealed class TextMessageTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="TextMessageTermination"/> class.
/// </summary>
/// <param name="source">
/// The source name to match against incoming messages. If <c>null</c>, matches any source.
/// Defaults to <c>null</c>.
/// </param>
public TextMessageTermination(string? source = null)
{
this.Source = source;
this.IsTerminated = false;
}
public string? Source { get; }
public bool IsTerminated { get; private set; }
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
foreach (AgentMessage item in messages)
{
if (item is TextMessage textMessage && (this.Source == null || textMessage.Source == this.Source))
{
this.IsTerminated = true;
string message = $"Text message received from '{textMessage.Source}'.";
StopMessage result = new() { Content = message, Source = nameof(TextMessageTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.IsTerminated = false;
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// TimeoutTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation after the specified duration has passed.
/// </summary>
public sealed class TimeoutTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeoutTermination"/> class.
/// </summary>
/// <param name="timeout">The maximum duration before terminating the conversation.</param>
public TimeoutTermination(TimeSpan timeout)
{
this.Timeout = timeout;
this.StartTime = DateTime.UtcNow;
}
/// <summary>
/// Initializes a new instance of the <see cref="TimeoutTermination"/> class.
/// </summary>
/// <param name="seconds">The maximum duration in seconds before terminating the conversation.</param>
public TimeoutTermination(float seconds) : this(TimeSpan.FromSeconds(seconds))
{
}
public TimeSpan Timeout { get; }
public DateTime StartTime { get; private set; }
public bool IsTerminated { get; private set; }
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
if (DateTime.UtcNow - this.StartTime >= this.Timeout)
{
this.IsTerminated = true;
string message = $"Timeout of {this.Timeout.TotalSeconds} seconds reached.";
StopMessage result = new() { Content = message, Source = nameof(TimeoutTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.IsTerminated = false;
this.StartTime = DateTime.UtcNow;
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// TokenUsageTermination.cs
using Microsoft.AutoGen.AgentChat.Abstractions;
namespace Microsoft.AutoGen.AgentChat.Terminations;
/// <summary>
/// Terminate the conversation if the token usage limit is reached.
/// </summary>
public sealed class TokenUsageTermination : ITerminationCondition
{
/// <summary>
/// Initializes a new instance of the <see cref="TokenUsageTermination"/> class.
/// </summary>
/// <param name="maxTotalTokens">The maximum total number of tokens allowed in the conversation.</param>
/// <param name="maxPromptTokens">The maximum number of prompt tokens allowed in the conversation.</param>
/// <param name="maxCompletionTokens">The maximum number of completion tokens allowed in the conversation.</param>
public TokenUsageTermination(int? maxTotalTokens = null, int? maxPromptTokens = null, int? maxCompletionTokens = null)
{
this.MaxTotalTokens = maxTotalTokens;
this.MaxPromptTokens = maxPromptTokens;
this.MaxCompletionTokens = maxCompletionTokens;
this.PromptTokenCount = 0;
this.CompletionTokenCount = 0;
}
public int? MaxTotalTokens { get; }
public int? MaxPromptTokens { get; }
public int? MaxCompletionTokens { get; }
public int TotalTokenCount => this.PromptTokenCount + this.CompletionTokenCount;
public int PromptTokenCount { get; private set; }
public int CompletionTokenCount { get; private set; }
public bool IsTerminated =>
(this.MaxTotalTokens != null && this.TotalTokenCount >= this.MaxTotalTokens) ||
(this.MaxPromptTokens != null && this.PromptTokenCount >= this.MaxPromptTokens) ||
(this.MaxCompletionTokens != null && this.CompletionTokenCount >= this.MaxCompletionTokens);
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages)
{
if (this.IsTerminated)
{
throw new TerminatedException();
}
foreach (AgentMessage item in messages)
{
if (item.ModelUsage is RequestUsage usage)
{
this.PromptTokenCount += usage.PromptTokens;
this.CompletionTokenCount += usage.CompletionTokens;
}
}
if (this.IsTerminated)
{
string message = $"Token usage limit reached, total token count: {this.TotalTokenCount}, prompt token count: {this.PromptTokenCount}, completion token count: {this.CompletionTokenCount}.";
StopMessage result = new() { Content = message, Source = nameof(TokenUsageTermination) };
return ValueTask.FromResult<StopMessage?>(result);
}
return ValueTask.FromResult<StopMessage?>(null);
}
public void Reset()
{
this.PromptTokenCount = 0;
this.CompletionTokenCount = 0;
}
}

View File

@ -0,0 +1,31 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**
/python

View File

@ -0,0 +1,34 @@
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
# This stage is used when running from VS in fast mode (Default for Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 5001
# This stage is used to build the service project
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["dotnet/Directory.Packages.props", "dotnet/"]
COPY ["dotnet/Directory.Build.props", "dotnet/"]
COPY ["dotnet/Directory.Build.targets", "dotnet/"]
COPY ["dotnet/NuGet.config", "dotnet/"]
COPY ["dotnet/src/Microsoft.Autogen.AgentHost/Microsoft.Autogen.AgentHost.csproj", "dotnet/src/Microsoft.Autogen.AgentHost/"]
COPY ["dotnet/src/Microsoft.AutoGen.Runtime.Grpc/Microsoft.AutoGen.RuntimeGateway.Grpc.csproj", "dotnet/src/Microsoft.AutoGen.RuntimeGateway.Grpc/"]
COPY ["dotnet/src/Microsoft.AutoGen.Contracts/Microsoft.AutoGen.Contracts.csproj", "dotnet/src/Microsoft.AutoGen.Contracts/"]
RUN dotnet restore "./dotnet/src/Microsoft.Autogen.AgentHost/Microsoft.Autogen.AgentHost.csproj"
COPY . .
WORKDIR "/src/dotnet/src/Microsoft.Autogen.AgentHost"
RUN dotnet build "./Microsoft.Autogen.AgentHost.csproj" -c $BUILD_CONFIGURATION -o /app/build
# This stage is used to publish the service project to be copied to the final stage
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Microsoft.Autogen.AgentHost.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Microsoft.Autogen.AgentHost.dll"]

View File

@ -6,7 +6,16 @@
<ContainerRepository>autogen-host</ContainerRepository>
<ContainerFamily>alpine</ContainerFamily>
<EnableSdkContainerSupport>true</EnableSdkContainerSupport>
</PropertyGroup>
<PackAsTool>true</PackAsTool>
<ToolCommandName>agenthost</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
<Import Project="$(RepoRoot)/nuget/nuget-package.props" />
<ItemGroup>
<ContainerEnvironmentVariable Include="ASPNETCORE_HTTP_PORTS" Value="5001" />
<ContainerPort Include="5001" Type="tcp" />

View File

@ -26,19 +26,19 @@ public abstract class InferenceAgent<T>(
{
protected IChatClient ChatClient { get; } = client;
private ILogger<InferenceAgent<T>>? Logger => _logger as ILogger<InferenceAgent<T>>;
private Task<ChatCompletion> CompleteAsync(
private Task<ChatResponse> CompleteAsync(
IList<ChatMessage> chatMessages,
ChatOptions? options = null,
CancellationToken cancellationToken = default)
{
return ChatClient.CompleteAsync(chatMessages, options, cancellationToken);
return ChatClient.GetResponseAsync(chatMessages, options, cancellationToken);
}
private IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
private IAsyncEnumerable<ChatResponseUpdate> CompleteStreamingAsync(
IList<ChatMessage> chatMessages,
ChatOptions? options = null,
CancellationToken cancellationToken = default)
{
return ChatClient.CompleteStreamingAsync(chatMessages, options, cancellationToken);
return ChatClient.GetStreamingResponseAsync(chatMessages, options, cancellationToken);
}
}

View File

@ -11,11 +11,10 @@
<ItemGroup>
<ProjectReference Include="..\Contracts\Microsoft.AutoGen.Contracts.csproj" />
<ProjectReference Include="..\Core\Microsoft.AutoGen.Core.csproj" />
<ProjectReference Include="..\Extensions\Aspire\Microsoft.AutoGen.Extensions.Aspire.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SemanticKernel" />
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" />
<PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>

View File

@ -6,7 +6,7 @@ namespace Microsoft.AutoGen.Contracts;
/// <summary>
/// Represents an agent within the runtime that can process messages, maintain state, and be closed when no longer needed.
/// </summary>
public interface IAgent : ISaveState<IAgent>
public interface IAgent : ISaveState
{
/// <summary>
/// Gets the unique identifier of the agent.
@ -42,6 +42,6 @@ public interface IHostableAgent : IAgent
/// Called when the runtime is closing.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public ValueTask CloseAsync() => ValueTask.CompletedTask;
public ValueTask CloseAsync();
}

View File

@ -8,7 +8,7 @@ namespace Microsoft.AutoGen.Contracts;
/// <summary>
/// Defines the runtime environment for agents, managing message sending, subscriptions, agent resolution, and state persistence.
/// </summary>
public interface IAgentRuntime : ISaveState<IAgentRuntime>
public interface IAgentRuntime : ISaveState
{
/// <summary>
/// Sends a message to an agent and gets a response.

View File

@ -9,9 +9,10 @@ namespace Microsoft.AutoGen.Contracts;
/// Defines a contract for saving and loading the state of an object.
/// The state must be JSON serializable.
/// </summary>
/// <typeparam name="T">The type of the object implementing this interface.</typeparam>
public interface ISaveState<T>
public interface ISaveState
{
public static ValueTask<JsonElement> DefaultSaveStateAsync() => new(JsonDocument.Parse("{}").RootElement);
/// <summary>
/// Saves the current state of the object.
/// </summary>
@ -20,7 +21,7 @@ public interface ISaveState<T>
/// containing the saved state. The structure of the state is implementation-defined
/// but must be JSON serializable.
/// </returns>
public ValueTask<JsonElement> SaveStateAsync();
public virtual ValueTask<JsonElement> SaveStateAsync() => DefaultSaveStateAsync();
/// <summary>
/// Loads a previously saved state into the object.
@ -30,6 +31,6 @@ public interface ISaveState<T>
/// is implementation-defined but must be JSON serializable.
/// </param>
/// <returns>A task representing the asynchronous operation.</returns>
public ValueTask LoadStateAsync(JsonElement state);
public virtual ValueTask LoadStateAsync(JsonElement state) => ValueTask.CompletedTask;
}

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ISaveStateMixin.cs
using System.Text.Json;
namespace Microsoft.AutoGen.Contracts;
/// <summary>
/// Defines a contract for saving and loading the state of an object.
/// The state must be JSON serializable.
/// </summary>
/// <typeparam name="T">The type of the object implementing this interface.</typeparam>
///
public interface ISaveStateMixin<T> : ISaveState
{
/// <summary>
/// Saves the current state of the object.
/// </summary>
/// <returns>
/// A task representing the asynchronous operation, returning a dictionary
/// containing the saved state. The structure of the state is implementation-defined
/// but must be JSON serializable.
/// </returns>
async ValueTask<JsonElement> ISaveState.SaveStateAsync()
{
var state = await SaveStateImpl();
return JsonSerializer.SerializeToElement(state);
}
/// <summary>
/// Loads a previously saved state into the object.
/// </summary>
/// <param name="state">
/// A dictionary representing the saved state. The structure of the state
/// is implementation-defined but must be JSON serializable.
/// </param>
/// <returns>A task representing the asynchronous operation.</returns>
ValueTask ISaveState.LoadStateAsync(JsonElement state)
{
// Throw if failed to deserialize
var stateObject = JsonSerializer.Deserialize<T>(state) ?? throw new InvalidDataException();
return LoadStateImpl(stateObject);
}
protected ValueTask<T> SaveStateImpl();
protected ValueTask LoadStateImpl(T state);
}

View File

@ -13,7 +13,7 @@ internal static class KVStringParseHelper
/// <summary>
/// The regular expression pattern used to match key-value pairs in the format "key/value".
/// </summary>
private const string KVPairPattern = @"^(?<key>\w+)/(?<value>\w+)$";
private const string KVPairPattern = @"^(?<key>\w+)/(?<value>[\w-]+)$";
/// <summary>
/// The compiled regex used for extracting key-value pairs from a string.

View File

@ -4,7 +4,6 @@
using System.Diagnostics;
using System.Reflection;
using System.Text.Json;
using Microsoft.AutoGen.Contracts;
using Microsoft.Extensions.Logging;
@ -13,7 +12,7 @@ namespace Microsoft.AutoGen.Core;
/// <summary>
/// Represents the base class for an agent in the AutoGen system.
/// </summary>
public abstract class BaseAgent : IAgent, IHostableAgent
public abstract class BaseAgent : IHostableAgent, ISaveState
{
/// <summary>
/// The activity source for tracing.
@ -93,15 +92,6 @@ public abstract class BaseAgent : IAgent, IHostableAgent
return null;
}
public virtual ValueTask<JsonElement> SaveStateAsync()
{
return ValueTask.FromResult(JsonDocument.Parse("{}").RootElement);
}
public virtual ValueTask LoadStateAsync(JsonElement state)
{
return ValueTask.CompletedTask;
}
public ValueTask<object?> SendMessageAsync(object message, AgentId recepient, string? messageId = null, CancellationToken cancellationToken = default)
{
return this.Runtime.SendMessageAsync(message, recepient, sender: this.Id, messageId: messageId, cancellationToken: cancellationToken);
@ -112,4 +102,19 @@ public abstract class BaseAgent : IAgent, IHostableAgent
{
return this.Runtime.PublishMessageAsync(message, topic, sender: this.Id, messageId: messageId, cancellationToken: cancellationToken);
}
//public virtual ValueTask<JsonElement> SaveStateAsync()
//{
// return new ValueTask<JsonElement>(JsonDocument.Parse("{}").RootElement);
//}
//public virtual ValueTask LoadStateAsync(JsonElement _)
//{
// return ValueTask.CompletedTask;
//}
public virtual ValueTask CloseAsync()
{
return ValueTask.CompletedTask;
}
}

View File

@ -14,8 +14,8 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
public bool DeliverToSelf { get; set; } //= false;
internal Dictionary<AgentId, IHostableAgent> agentInstances = new();
Dictionary<string, ISubscriptionDefinition> subscriptions = new();
Dictionary<AgentType, Func<AgentId, IAgentRuntime, ValueTask<IHostableAgent>>> agentFactories = new();
private Dictionary<string, ISubscriptionDefinition> subscriptions = new();
private Dictionary<AgentType, Func<AgentId, IAgentRuntime, ValueTask<IHostableAgent>>> agentFactories = new();
private ValueTask<T> ExecuteTracedAsync<T>(Func<ValueTask<T>> func)
{
@ -43,28 +43,45 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
}
TopicId topic = envelope.Topic.Value;
List<Exception> exceptions = new();
foreach (var subscription in this.subscriptions.Values.Where(subscription => subscription.Matches(topic)))
{
AgentId? sender = envelope.Sender;
CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(envelope.Cancellation, deliveryToken);
MessageContext messageContext = new(envelope.MessageId, combinedSource.Token)
try
{
Sender = sender,
Topic = topic,
IsRpc = false
};
deliveryToken.ThrowIfCancellationRequested();
AgentId agentId = subscription.MapToAgent(topic);
if (!this.DeliverToSelf && sender.HasValue && sender == agentId)
{
continue;
AgentId? sender = envelope.Sender;
CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(envelope.Cancellation, deliveryToken);
MessageContext messageContext = new(envelope.MessageId, combinedSource.Token)
{
Sender = sender,
Topic = topic,
IsRpc = false
};
AgentId agentId = subscription.MapToAgent(topic);
if (!this.DeliverToSelf && sender.HasValue && sender == agentId)
{
continue;
}
IHostableAgent agent = await this.EnsureAgentAsync(agentId);
// TODO: Cancellation propagation!
await agent.OnMessageAsync(envelope.Message, messageContext);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
IHostableAgent agent = await this.EnsureAgentAsync(agentId);
// TODO: Cancellation propagation!
await agent.OnMessageAsync(envelope.Message, messageContext);
if (exceptions.Count > 0)
{
// TODO: Unwrap TargetInvocationException?
throw new AggregateException("One or more exceptions occurred while processing the message.", exceptions);
}
}
@ -78,7 +95,7 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
this.messageDeliveryQueue.Enqueue(delivery);
return ValueTask.CompletedTask;
return delivery.FutureNoResult;
});
}
@ -104,14 +121,15 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
public ValueTask<object?> SendMessageAsync(object message, AgentId recepient, AgentId? sender = null, string? messageId = null, CancellationToken cancellationToken = default)
{
return this.ExecuteTracedAsync(() =>
return this.ExecuteTracedAsync(async () =>
{
MessageDelivery delivery = new MessageEnvelope(message, messageId, cancellationToken)
.WithSender(sender)
.ForSend(recepient, this.SendMessageServicer);
this.messageDeliveryQueue.Enqueue(delivery);
return delivery.Future;
return await delivery.Future;
});
}
@ -215,7 +233,9 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
var agentState = await this.agentInstances[agentId].SaveStateAsync();
state[agentId.ToString()] = agentState;
}
return JsonSerializer.SerializeToElement(state);
JsonElement jsonElement = JsonSerializer.SerializeToElement(state);
return jsonElement;
}
public ValueTask<AgentType> RegisterAgentFactoryAsync<TAgent>(AgentType type, Func<AgentId, IAgentRuntime, ValueTask<TAgent>> factoryFunc) where TAgent : IHostableAgent
@ -240,7 +260,7 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
return ValueTask.FromResult(new AgentProxy(agentId, this));
}
public ValueTask ProcessNextMessage(CancellationToken cancellation = default)
public ValueTask ProcessNextMessageAsync(CancellationToken cancellation = default)
{
Debug.WriteLine("Processing next message...");
if (this.messageDeliveryQueue.TryDequeue(out MessageDelivery? delivery))
@ -256,11 +276,34 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
private CancellationTokenSource? finishSource;
private async Task RunAsync(CancellationToken cancellation)
{
Dictionary<Guid, Task> pendingTasks = new();
while (!cancellation.IsCancellationRequested && shouldContinue())
{
await this.ProcessNextMessage(cancellation);
// Get a unique task id
Guid taskId;
do
{
taskId = Guid.NewGuid();
} while (pendingTasks.ContainsKey(taskId));
// There is potentially a race condition here, but even if we leak a Task, we will
// still catch it on the Finish() pass.
ValueTask processTask = this.ProcessNextMessageAsync(cancellation);
await Task.Yield();
// Check if the task is already completed
if (processTask.IsCompleted)
{
continue;
}
else
{
Task actualTask = processTask.AsTask();
pendingTasks.Add(taskId, actualTask.ContinueWith(t => pendingTasks.Remove(taskId), TaskScheduler.Current));
}
}
await Task.WhenAll(pendingTasks.Values.Where(t => t is not null).ToArray());
await this.FinishAsync(this.finishSource?.Token ?? CancellationToken.None);
}
@ -300,12 +343,15 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
return ValueTask.CompletedTask;
}
public Task RunUntilIdleAsync()
public async Task RunUntilIdleAsync()
{
Func<bool> oldShouldContinue = this.shouldContinue;
this.shouldContinue = () => !this.messageDeliveryQueue.IsEmpty;
// TODO: Do we want detach semantics?
return this.messageDeliveryTask;
await this.messageDeliveryTask;
this.shouldContinue = oldShouldContinue;
}
private async Task FinishAsync(CancellationToken token)
@ -317,6 +363,9 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
await agent.CloseAsync();
}
}
this.shutdownSource = null;
this.finishSource = null;
}
Task IHostedService.StartAsync(CancellationToken cancellationToken) => this.StartAsync(cancellationToken).AsTask();

View File

@ -1,17 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// MessageDelivery.cs
using System.Reflection;
using Microsoft.AutoGen.Contracts;
namespace Microsoft.AutoGen.Core;
internal sealed class MessageDelivery(MessageEnvelope message, Func<MessageEnvelope, CancellationToken, ValueTask> servicer, IResultSink<object?>? resultSink = null)
internal sealed class MessageDelivery(MessageEnvelope message, Func<MessageEnvelope, CancellationToken, ValueTask> servicer, IResultSink<object?> resultSink)
{
public MessageEnvelope Message { get; } = message;
public Func<MessageEnvelope, CancellationToken, ValueTask> Servicer { get; } = servicer;
public IResultSink<object?>? ResultSink { get; } = resultSink;
public IResultSink<object?> ResultSink { get; } = resultSink;
public ValueTask<object?> Future => this.ResultSink != null ? this.ResultSink.Future : ValueTask.FromResult((object?)null);
public ValueTask<object?> Future => this.ResultSink.Future;
public ValueTask FutureNoResult => this.ResultSink.FutureNoResult;
public ValueTask InvokeAsync(CancellationToken cancellation)
{
@ -48,8 +50,19 @@ internal sealed class MessageEnvelope
ResultSink<object?> resultSink = new ResultSink<object?>();
Func<MessageEnvelope, CancellationToken, ValueTask> boundServicer = async (envelope, cancellation) =>
{
object? result = await servicer(envelope, cancellation);
resultSink.SetResult(result);
try
{
object? result = await servicer(envelope, cancellation);
resultSink.SetResult(result);
}
catch (TargetInvocationException ex) when (ex.InnerException is OperationCanceledException innerOCEx)
{
resultSink.SetCancelled(innerOCEx);
}
catch (Exception ex)
{
resultSink.SetException(ex);
}
};
return new MessageDelivery(this, boundServicer, resultSink);
@ -59,6 +72,20 @@ internal sealed class MessageEnvelope
{
this.Topic = topic;
return new MessageDelivery(this, servicer);
ResultSink<object?> waitForPublish = new ResultSink<object?>();
Func<MessageEnvelope, CancellationToken, ValueTask> boundServicer = async (envelope, cancellation) =>
{
try
{
await servicer(envelope, cancellation);
waitForPublish.SetResult(null);
}
catch (Exception ex) // Do we want to special-case cancellation, and hoist the exception type like above?
{
waitForPublish.SetException(ex);
}
};
return new MessageDelivery(this, boundServicer, waitForPublish);
}
}

View File

@ -5,13 +5,14 @@ using System.Threading.Tasks.Sources;
namespace Microsoft.AutoGen.Core;
internal interface IResultSink<TResult> : IValueTaskSource<TResult>
internal interface IResultSink<TResult> : IValueTaskSource<TResult>, IValueTaskSource
{
public void SetResult(TResult result);
public void SetException(Exception exception);
public void SetCancelled();
public void SetCancelled(OperationCanceledException? ocEx = null);
public ValueTask<TResult> Future { get; }
public ValueTask FutureNoResult { get; }
}
public sealed class ResultSink<TResult> : IResultSink<TResult>
@ -34,10 +35,10 @@ public sealed class ResultSink<TResult> : IResultSink<TResult>
}
public bool IsCancelled { get; private set; }
public void SetCancelled()
public void SetCancelled(OperationCanceledException? ocEx = null)
{
this.IsCancelled = true;
this.core.SetException(new OperationCanceledException());
this.core.SetException(ocEx ?? new OperationCanceledException());
}
public void SetException(Exception exception)
@ -50,5 +51,9 @@ public sealed class ResultSink<TResult> : IResultSink<TResult>
this.core.SetResult(result);
}
void IValueTaskSource.GetResult(short token) => _ = this.GetResult(token);
public ValueTask<TResult> Future => new(this, this.core.Version);
public ValueTask FutureNoResult => new(this, this.core.Version);
}

View File

@ -41,12 +41,13 @@ public static class ServiceCollectionChatClientExtensions
Func<ChatClientBuilder, ChatClientBuilder>? builder = null)
{
uri ??= new Uri("http://localhost:11434");
return services.AddChatClient(pipeline =>
services.AddChatClient(service =>
{
builder?.Invoke(pipeline);
var httpClient = pipeline.Services.GetService<HttpClient>() ?? new();
return pipeline.Use(new OllamaChatClient(uri, modelName, httpClient));
var httpClient = service.GetService<HttpClient>() ?? new();
return new OllamaChatClient(uri, modelName, httpClient);
});
return services;
}
public static IServiceCollection AddOpenAIChatClient(
this IHostApplicationBuilder hostBuilder,
@ -81,16 +82,17 @@ public static class ServiceCollectionChatClientExtensions
Uri? endpoint = null,
Func<ChatClientBuilder, ChatClientBuilder>? builder = null)
{
return services
services
.AddSingleton(_ => endpoint is null
? new OpenAIClient(apiKey)
: new AzureOpenAIClient(endpoint, new ApiKeyCredential(apiKey)))
.AddChatClient(pipeline =>
.AddChatClient(service =>
{
builder?.Invoke(pipeline);
var openAiClient = pipeline.Services.GetRequiredService<OpenAIClient>();
return pipeline.Use(openAiClient.AsChatClient(modelOrDeploymentName));
var openAiClient = service.GetRequiredService<OpenAIClient>();
return openAiClient.AsChatClient(modelOrDeploymentName);
});
return services;
}
public static IServiceCollection AddAzureChatClient(
this IHostApplicationBuilder hostBuilder,
@ -109,12 +111,10 @@ public static class ServiceCollectionChatClientExtensions
}
var endpoint = $"{serviceName}:Endpoint" ?? throw new InvalidOperationException($"No endpoint was specified for the Azure Inference Chat Client");
var endpointUri = string.IsNullOrEmpty(endpoint) ? null : new Uri(endpoint);
return hostBuilder.Services.AddChatClient(pipeline =>
{
builder?.Invoke(pipeline);
var token = Environment.GetEnvironmentVariable("GH_TOKEN") ?? throw new InvalidOperationException("No model access token was found in the environment variable GH_TOKEN");
return pipeline.Use(new ChatCompletionsClient(
endpointUri, new AzureKeyCredential(token)).AsChatClient(modelOrDeploymentName));
});
var token = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new InvalidOperationException("No model access token was found in the environment variable AZURE_OPENAI_API_KEY");
var chatClient = new ChatCompletionsClient(endpointUri, new AzureKeyCredential(token)).AsChatClient(modelOrDeploymentName);
hostBuilder.Services.AddChatClient(chatClient);
return hostBuilder.Services;
}
}

View File

@ -1,7 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// IConnection.cs
namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
public interface IConnection
{
}

View File

@ -5,11 +5,45 @@ using Microsoft.AutoGen.Protobuf;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
/// <summary>
/// Defines the gateway interface for handling RPC requests and subscriptions.
/// Note that all of the request types are generated from the proto file.
/// </summary>
public interface IGateway : IGrainObserver
{
/// <summary>
/// Invokes a request asynchronously.
/// </summary>
/// <param name="request">The RPC request.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the RPC response.</returns>
ValueTask<RpcResponse> InvokeRequestAsync(RpcRequest request);
/// <summary>
/// Registers an agent type asynchronously.
/// </summary>
/// <param name="request">The register agent type request.</param>
/// <param name="context">The server call context.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the register agent type response.</returns>
ValueTask<RegisterAgentTypeResponse> RegisterAgentTypeAsync(RegisterAgentTypeRequest request, ServerCallContext context);
/// <summary>
/// Subscribes to a topic asynchronously.
/// </summary>
/// <param name="request">The add subscription request.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the add subscription response.</returns>
ValueTask<AddSubscriptionResponse> SubscribeAsync(AddSubscriptionRequest request);
/// <summary>
/// Unsubscribes from a topic asynchronously.
/// </summary>
/// <param name="request">The remove subscription request.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the remove subscription response.</returns>
ValueTask<RemoveSubscriptionResponse> UnsubscribeAsync(RemoveSubscriptionRequest request);
/// <summary>
/// Gets the subscriptions asynchronously.
/// </summary>
/// <param name="request">The get subscriptions request.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the list of subscriptions.</returns>
ValueTask<List<Subscription>> GetSubscriptionsAsync(GetSubscriptionsRequest request);
}

View File

@ -5,7 +5,7 @@ using Microsoft.AutoGen.Protobuf;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
/// <summary>
/// Interface for managing agent registration, placement, and subscriptions.
/// Keeps track of which agents are registered with which gateways.
/// </summary>
public interface IGatewayRegistry : IRegistry
{
@ -27,6 +27,7 @@ public interface IGatewayRegistry : IRegistry
/// Registers a new agent type with the specified worker.
/// </summary>
/// <param name="request">The request containing agent type details.</param>
/// <param name="clientId">The client ID of the worker.</param>
/// <param name="worker">The worker to register the agent type with.</param>
/// <returns>A task representing the asynchronous operation.</returns>
ValueTask RegisterAgentTypeAsync(RegisterAgentTypeRequest request, string clientId, IGateway worker);

View File

@ -5,7 +5,6 @@ namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
public interface IRegistry
{
/// <summary>
/// Gets a list of agents subscribed to and handling the specified topic and event type.
/// </summary>
@ -28,11 +27,12 @@ public interface IRegistry
/// <param name="request">The unsubscription request.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>removing CancellationToken from here as it is not compatible with Orleans Serialization</remarks>
ValueTask UnsubscribeAsync(RemoveSubscriptionRequest request); // TODO: This should have its own request type.
ValueTask UnsubscribeAsync(RemoveSubscriptionRequest request); // TODO: This should have its own request type.
/// <summary>
/// Gets the subscriptions for a specified agent type.
/// </summary>
/// <param name="request">The get subscriptions request.</param>
/// <returns>A task representing the asynchronous operation, with the subscriptions as the result.</returns>
ValueTask<List<Subscription>> GetSubscriptionsAsync(GetSubscriptionsRequest request);
}

View File

@ -10,7 +10,6 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc;
/// <summary>
/// Represents the gRPC gateway service that handles communication between the agent worker and the cluster.
/// </summary>
@ -31,10 +30,11 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// </summary>
private readonly IMessageRegistryGrain _messageRegistry;
private readonly IGateway _reference;
private readonly ConcurrentDictionary<string, List<GrpcWorkerConnection>> _supportedAgentTypes = [];
public readonly ConcurrentDictionary<string, GrpcWorkerConnection> _workers = new();
private readonly ConcurrentDictionary<(string Type, string Key), GrpcWorkerConnection> _agentDirectory = new();
private readonly ConcurrentDictionary<(GrpcWorkerConnection, string), TaskCompletionSource<RpcResponse>> _pendingRequests = new();
private readonly ConcurrentDictionary<string, List<GrpcWorkerConnection<Message>>> _supportedAgentTypes = [];
public readonly ConcurrentDictionary<string, GrpcWorkerConnection<Message>> _workers = new();
public readonly ConcurrentDictionary<string, GrpcWorkerConnection<ControlMessage>> _controlWorkers = new();
private readonly ConcurrentDictionary<(string Type, string Key), GrpcWorkerConnection<Message>> _agentDirectory = new();
private readonly ConcurrentDictionary<(GrpcWorkerConnection<Message>, string), TaskCompletionSource<RpcResponse>> _pendingRequests = new();
/// <summary>
/// Initializes a new instance of the <see cref="GrpcGateway"/> class.
@ -255,15 +255,27 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// <param name="responseStream">The response stream.</param>
/// <param name="context">The server call context.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
internal async Task ConnectToWorkerProcess(IAsyncStreamReader<Message> requestStream, IServerStreamWriter<Message> responseStream, ServerCallContext context)
internal async Task ConnectToWorkerProcess<TMessage>(IAsyncStreamReader<TMessage> requestStream, IServerStreamWriter<TMessage> responseStream, ServerCallContext context)
where TMessage : class
{
_logger.LogInformation("Received new connection from {Peer}.", context.Peer);
var clientId = (context.RequestHeaders.Get("client-id")?.Value) ??
throw new RpcException(new Status(StatusCode.InvalidArgument, "Client ID is required."));
var workerProcess = new GrpcWorkerConnection(this, requestStream, responseStream, context);
_workers.GetOrAdd(clientId, workerProcess);
var clientId = context.RequestHeaders.Get("client-id")?.Value
?? throw new RpcException(new Status(StatusCode.InvalidArgument, "Client ID is required."));
var workerProcess = new GrpcWorkerConnection<TMessage>(this, requestStream, responseStream, context);
await this.AttachDanglingRegistrations(clientId).ConfigureAwait(false);
if (typeof(TMessage) == typeof(Message))
{
_workers.GetOrAdd(clientId, _ => (GrpcWorkerConnection<Message>)(object)workerProcess);
await this.AttachDanglingRegistrations(clientId).ConfigureAwait(false);
}
else if (typeof(TMessage) == typeof(ControlMessage))
{
_controlWorkers.GetOrAdd(clientId, _ => (GrpcWorkerConnection<ControlMessage>)(object)workerProcess);
}
else
{
throw new InvalidOperationException($"Unsupported message type: {typeof(TMessage).Name}");
}
await workerProcess.Connect().ConfigureAwait(false);
}
@ -301,22 +313,39 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// <param name="message">The received message.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
internal async Task OnReceivedMessageAsync(GrpcWorkerConnection connection, Message message, CancellationToken cancellationToken = default)
internal async Task OnReceivedMessageAsync<TMessage>(GrpcWorkerConnection<TMessage> connection, TMessage message, CancellationToken cancellationToken = default)
where TMessage : class
{
_logger.LogInformation("Received message {Message} from connection {Connection}.", message, connection);
switch (message.MessageCase)
switch (message)
{
case Message.MessageOneofCase.Request:
await DispatchRequestAsync(connection, message.Request);
case Message msg:
// Handle regular messages
switch (msg.MessageCase)
{
case Message.MessageOneofCase.Request:
await DispatchRequestAsync(connection, msg.Request);
break;
case Message.MessageOneofCase.Response:
DispatchResponse(connection, msg.Response);
break;
case Message.MessageOneofCase.CloudEvent:
await DispatchEventAsync(msg.CloudEvent, cancellationToken);
break;
default:
await RespondBadRequestAsync(connection, $"Unknown message type for message '{msg}'.");
break;
}
break;
case Message.MessageOneofCase.Response:
DispatchResponse(connection, message.Response);
break;
case Message.MessageOneofCase.CloudEvent:
await DispatchEventAsync(message.CloudEvent, cancellationToken);
case ControlMessage controlMsg:
// Handle control messages
await DispatchControlMessageAsync(connection, controlMsg, cancellationToken);
break;
default:
await RespondBadRequestAsync(connection, $"Unknown message type for message '{message}'.");
await RespondBadRequestAsync(connection, $"Unsupported message type: {typeof(TMessage).Name}");
break;
}
}
@ -326,14 +355,18 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// </summary>
/// <param name="connection">The worker connection.</param>
/// <param name="response">The RPC response.</param>
private void DispatchResponse(GrpcWorkerConnection connection, RpcResponse response)
private void DispatchResponse<TMessage>(GrpcWorkerConnection<TMessage> connection, RpcResponse response)
where TMessage : class
{
if (!_pendingRequests.TryRemove((connection, response.RequestId), out var completion))
if (connection is GrpcWorkerConnection<Message> messageConnection)
{
_logger.LogWarning("Received response for unknown request id: {RequestId}.", response.RequestId);
return;
if (!_pendingRequests.TryRemove((messageConnection, response.RequestId), out var completion))
{
_logger.LogWarning("Received response for unknown request id: {RequestId}.", response.RequestId);
return;
}
completion.SetResult(response);
}
completion.SetResult(response);
}
/// <summary>
@ -392,7 +425,8 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// <param name="connection">The worker connection.</param>
/// <param name="request">The RPC request.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private async ValueTask DispatchRequestAsync(GrpcWorkerConnection connection, RpcRequest request)
private async ValueTask DispatchRequestAsync<TMessage>(GrpcWorkerConnection<TMessage> connection, RpcRequest request)
where TMessage : class
{
var requestId = request.RequestId;
if (request.Target is null)
@ -422,17 +456,27 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// <param name="request">The RPC request.</param>
/// <param name="func">The function to invoke.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private static async Task InvokeRequestDelegate(GrpcWorkerConnection connection, RpcRequest request, Func<RpcRequest, Task<RpcResponse>> func)
private static async Task InvokeRequestDelegate<TMessage>(GrpcWorkerConnection<TMessage> connection, RpcRequest request, Func<RpcRequest, Task<RpcResponse>> func)
where TMessage : class
{
try
{
var response = await func(request);
response.RequestId = request.RequestId;
await connection.ResponseStream.WriteAsync(new Message { Response = response }).ConfigureAwait(false);
if (connection is GrpcWorkerConnection<Message> messageConnection)
{
await messageConnection.ResponseStream.WriteAsync(new Message { Response = response }).ConfigureAwait(false);
}
}
catch (Exception ex)
{
await connection.ResponseStream.WriteAsync(new Message { Response = new RpcResponse { RequestId = request.RequestId, Error = ex.Message } }).ConfigureAwait(false);
if (connection is GrpcWorkerConnection<Message> messageConnection)
{
await messageConnection.ResponseStream.WriteAsync(
new Message { Response = new RpcResponse { RequestId = request.RequestId, Error = ex.Message } }
).ConfigureAwait(false);
}
}
}
@ -440,24 +484,32 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// Handles the removal of a worker process.
/// </summary>
/// <param name="workerProcess">The worker process.</param>
internal void OnRemoveWorkerProcess(GrpcWorkerConnection workerProcess)
internal void OnRemoveWorkerProcess<TMessage>(GrpcWorkerConnection<TMessage> workerProcess)
where TMessage : class
{
var clientId = workerProcess.ServerCallContext.RequestHeaders.Get("client-id")?.Value ??
throw new RpcException(new Status(StatusCode.InvalidArgument, "Grpc Client ID is required."));
var clientId = workerProcess.ServerCallContext.RequestHeaders.Get("client-id")?.Value
?? throw new RpcException(new Status(StatusCode.InvalidArgument, "Grpc Client ID is required."));
_workers.TryRemove(clientId, out _);
_controlWorkers.TryRemove(clientId, out _);
var types = workerProcess.GetSupportedTypes();
foreach (var type in types)
{
if (_supportedAgentTypes.TryGetValue(type, out var supported))
if (_supportedAgentTypes.TryGetValue(type, out var supported) && workerProcess is GrpcWorkerConnection<Message> messageWorker)
{
supported.Remove(workerProcess);
supported.Remove(messageWorker);
}
}
foreach (var pair in _agentDirectory)
if (workerProcess is GrpcWorkerConnection<Message> messageWorkerInstance)
{
if (pair.Value == workerProcess)
foreach (var pair in _agentDirectory.ToList())
{
((IDictionary<(string Type, string Key), GrpcWorkerConnection>)_agentDirectory).Remove(pair);
if (ReferenceEquals(pair.Value, messageWorkerInstance)) // Ensures exact instance match
{
_agentDirectory.TryRemove(pair.Key, out _);
}
}
}
}
@ -468,7 +520,8 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// <param name="connection">The worker connection.</param>
/// <param name="error">The error message.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private static async ValueTask RespondBadRequestAsync(GrpcWorkerConnection connection, string error)
private static async ValueTask RespondBadRequestAsync<TMessage>(GrpcWorkerConnection<TMessage> connection, string error)
where TMessage : class
{
throw new RpcException(new Status(StatusCode.InvalidArgument, error));
}
@ -480,8 +533,28 @@ public sealed class GrpcGateway : BackgroundService, IGateway
/// <param name="cloudEvent">The cloud event.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private async Task WriteResponseAsync(GrpcWorkerConnection connection, CloudEvent cloudEvent, CancellationToken cancellationToken = default)
private async Task WriteResponseAsync(GrpcWorkerConnection<Message> connection, CloudEvent cloudEvent, CancellationToken cancellationToken = default)
{
await connection.ResponseStream.WriteAsync(new Message { CloudEvent = cloudEvent }, cancellationToken).ConfigureAwait(false);
}
private async ValueTask DispatchControlMessageAsync<TMessage>(GrpcWorkerConnection<TMessage> connection, ControlMessage controlMsg, CancellationToken cancellationToken)
where TMessage : class
{
if (string.IsNullOrEmpty(controlMsg.Destination))
{
throw new InvalidOperationException($"Control message is missing a destination. Message: '{controlMsg}'");
}
// Ensure the control message is of the correct type
if (controlMsg is TMessage typedResponseMessage)
{
// Send the response back to the client
await connection.ResponseStream.WriteAsync(typedResponseMessage, cancellationToken).ConfigureAwait(false);
}
else
{
throw new InvalidOperationException($"Cannot convert control message to type {typeof(TMessage).Name}");
}
}
}

View File

@ -35,6 +35,29 @@ public sealed class GrpcGatewayService(GrpcGateway gateway) : AgentRpc.AgentRpcB
}
}
/// <summary>
/// Open channel for the Control Channel (defined in the proto file).
/// </summary>
/// <param name="requestStream">The request stream.</param>
/// <param name="responseStream">The response stream.</param>
/// <param name="context">The server call context.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public override async Task OpenControlChannel(IAsyncStreamReader<ControlMessage> requestStream, IServerStreamWriter<ControlMessage> responseStream, ServerCallContext context)
{
try
{
await Gateway.ConnectToWorkerProcess(requestStream, responseStream, context).ConfigureAwait(true);
}
catch
{
if (context.CancellationToken.IsCancellationRequested)
{
return;
}
throw;
}
}
/// <summary>
/// Adds a subscription.
/// </summary>

View File

@ -2,12 +2,11 @@
// GrpcWorkerConnection.cs
using System.Threading.Channels;
using Grpc.Core;
using Microsoft.AutoGen.Protobuf;
using Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc;
public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
public sealed class GrpcWorkerConnection<TMessage> : IAsyncDisposable
where TMessage : class
{
private static long s_nextConnectionId;
private Task _readTask = Task.CompletedTask;
@ -18,13 +17,13 @@ public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
private readonly GrpcGateway _gateway;
private readonly CancellationTokenSource _shutdownCancellationToken = new();
public Task Completion { get; private set; } = Task.CompletedTask;
public GrpcWorkerConnection(GrpcGateway agentWorker, IAsyncStreamReader<Message> requestStream, IServerStreamWriter<Message> responseStream, ServerCallContext context)
public GrpcWorkerConnection(GrpcGateway agentWorker, IAsyncStreamReader<TMessage> requestStream, IServerStreamWriter<TMessage> responseStream, ServerCallContext context)
{
_gateway = agentWorker;
RequestStream = requestStream;
ResponseStream = responseStream;
ServerCallContext = context;
_outboundMessages = Channel.CreateUnbounded<Message>(new UnboundedChannelOptions { AllowSynchronousContinuations = true, SingleReader = true, SingleWriter = false });
_outboundMessages = Channel.CreateUnbounded<TMessage>(new UnboundedChannelOptions { AllowSynchronousContinuations = true, SingleReader = true, SingleWriter = false });
}
public Task Connect()
{
@ -51,12 +50,13 @@ public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
return Completion = Task.WhenAll(_readTask, _writeTask);
}
public IAsyncStreamReader<Message> RequestStream { get; }
public IServerStreamWriter<Message> ResponseStream { get; }
public IAsyncStreamReader<TMessage> RequestStream { get; }
public IServerStreamWriter<TMessage> ResponseStream { get; }
public ServerCallContext ServerCallContext { get; }
private readonly Channel<Message> _outboundMessages;
private readonly Channel<TMessage> _outboundMessages;
/// <inheritdoc/>
public void AddSupportedType(string type)
{
lock (_lock)
@ -65,6 +65,7 @@ public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
}
}
/// <inheritdoc/>
public HashSet<string> GetSupportedTypes()
{
lock (_lock)
@ -73,10 +74,13 @@ public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
}
}
public async Task SendMessage(Message message)
/// <inheritdoc/>
public async Task SendMessage(TMessage message)
{
await _outboundMessages.Writer.WriteAsync(message).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task RunReadPump()
{
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
@ -98,6 +102,7 @@ public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
}
}
/// <inheritdoc/>
public async Task RunWritePump()
{
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
@ -117,11 +122,13 @@ public sealed class GrpcWorkerConnection : IAsyncDisposable, IConnection
}
}
/// <inheritdoc/>
public async ValueTask DisposeAsync()
{
_shutdownCancellationToken.Cancel();
await Completion.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
}
/// <inheritdoc/>
public override string ToString() => $"Connection-{_connectionId}";
}

View File

@ -4,140 +4,79 @@
using Microsoft.AutoGen.Contracts;
using Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
using Microsoft.Extensions.Logging;
using Orleans.Concurrency;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc;
internal sealed class MessageRegistryGrain(
[PersistentState("state", "PubSubStore")] IPersistentState<MessageRegistryState> state,
ILogger<MessageRegistryGrain> logger
) : Grain, IMessageRegistryGrain
[Reentrant]
internal sealed class MessageRegistryGrain : Grain, IMessageRegistryGrain
{
// <summary>
// The number of times to retry writing the state before giving up.
// </summary>
public enum QueueType
{
DeadLetterQueue,
EventBuffer
}
private const int _retries = 5;
/// <summary>
/// The time to wait before removing a message from the event buffer.
/// in milliseconds
/// </summary>
private const int _bufferTime = 5000;
private readonly ILogger<MessageRegistryGrain> _logger = logger;
/// <summary>
/// maximum size of a message we will write to the state store in bytes
/// </summary>
/// <remarks>set this to HALF your intended limit as protobuf strings are UTF8 but .NET UTF16</remarks>
private const int _maxMessageSize = 1024 * 1024 * 10; // 10MB
/// <summary>
/// maximum size of a each queue
/// </summary>
/// <remarks>set this to HALF your intended limit as protobuf strings are UTF8 but .NET UTF16</remarks>
private const int _maxQueueSize = 1024 * 1024 * 10; // 10MB
private readonly MessageRegistryQueue _dlqQueue;
private readonly MessageRegistryQueue _ebQueue;
public MessageRegistryGrain(
[PersistentState("state", "PubSubStore")] IPersistentState<MessageRegistryState> state,
ILogger<MessageRegistryGrain> logger)
{
var stateManager = new StateManager(state);
_dlqQueue = new MessageRegistryQueue(
QueueType.DeadLetterQueue,
state,
stateManager,
logger,
_maxMessageSize,
_maxQueueSize);
_ebQueue = new MessageRegistryQueue(
QueueType.EventBuffer,
state,
stateManager,
logger,
_maxMessageSize,
_maxQueueSize);
}
// <inheritdoc />
public async Task AddMessageToDeadLetterQueueAsync(string topic, CloudEvent message)
{
await TryWriteMessageAsync("dlq", topic, message).ConfigureAwait(true);
await _dlqQueue.AddMessageAsync(topic, message);
}
///<inheritdoc />
public async Task AddMessageToEventBufferAsync(string topic, CloudEvent message)
{
await TryWriteMessageAsync("eb", topic, message).ConfigureAwait(true);
// Schedule the removal task to run in the background after bufferTime
_ = Task.Delay(_bufferTime)
.ContinueWith(
async _ => await RemoveMessage(topic, message),
TaskScheduler.Default
);
}
/// <summary>
/// remove a specific message from the buffer for a given topic
/// </summary>
/// <param name="topic"></param>
/// <param name="message"></param>
/// <returns>ValueTask<bool></returns>
private async ValueTask<bool> RemoveMessage(string topic, CloudEvent message)
{
if (state.State.EventBuffer != null && state.State.EventBuffer.TryGetValue(topic, out List<CloudEvent>? events))
{
if (events != null && events.Remove(message))
{
state.State.EventBuffer.AddOrUpdate(topic, events, (_, _) => events);
await state.WriteStateAsync().ConfigureAwait(true);
return true;
}
}
return false;
}
/// <summary>
/// Tries to write a message to the given queue in Orleans state.
/// Allows for retries using etag for optimistic concurrency.
/// </summary>
/// <param name="whichQueue"></param>
/// <param name="topic"></param>
/// <param name="message"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private async ValueTask<bool> TryWriteMessageAsync(string whichQueue, string topic, CloudEvent message)
{
var retries = _retries;
while (!await WriteMessageAsync(whichQueue, topic, message, state.Etag).ConfigureAwait(false))
{
if (retries-- <= 0)
{
throw new InvalidOperationException($"Failed to write MessageRegistryState after {_retries} retries.");
}
_logger.LogWarning("Failed to write MessageRegistryState. Retrying...");
retries--;
}
if (retries == 0) { return false; } else { return true; }
}
/// <summary>
/// Writes a message to the given queue in Orleans state.
/// </summary>
/// <param name="whichQueue"></param>
/// <param name="topic"></param>
/// <param name="message"></param>
/// <param name="etag"></param>
/// <returns>ValueTask<bool></returns>
/// <exception cref="ArgumentException"></exception>
private async ValueTask<bool> WriteMessageAsync(string whichQueue, string topic, CloudEvent message, string etag)
{
if (state.Etag != null && state.Etag != etag)
{
return false;
}
switch (whichQueue)
{
case "dlq":
var dlqQueue = state.State.DeadLetterQueue.GetOrAdd(topic, _ => new());
dlqQueue.Add(message);
state.State.DeadLetterQueue.AddOrUpdate(topic, dlqQueue, (_, _) => dlqQueue);
break;
case "eb":
var ebQueue = state.State.EventBuffer.GetOrAdd(topic, _ => new());
ebQueue.Add(message);
state.State.EventBuffer.AddOrUpdate(topic, ebQueue, (_, _) => ebQueue);
break;
default:
throw new ArgumentException($"Invalid queue name: {whichQueue}");
}
await state.WriteStateAsync().ConfigureAwait(true);
return true;
await _ebQueue.AddMessageAsync(topic, message);
_ebQueue.RemoveMessageAfterDelayAsync(topic, message, _bufferTime).Ignore();
}
// <inheritdoc />
public async Task<List<CloudEvent>> RemoveMessagesAsync(string topic)
{
var messages = new List<CloudEvent>();
if (state.State.DeadLetterQueue != null && state.State.DeadLetterQueue.Remove(topic, out List<CloudEvent>? letters))
{
await state.WriteStateAsync().ConfigureAwait(true);
if (letters != null)
{
messages.AddRange(letters);
}
}
if (state.State.EventBuffer != null && state.State.EventBuffer.Remove(topic, out List<CloudEvent>? events))
{
await state.WriteStateAsync().ConfigureAwait(true);
if (events != null)
{
messages.AddRange(events);
}
}
return messages;
var removedDeadLetter = await _dlqQueue.RemoveMessagesAsync(topic);
var removedBuffer = await _ebQueue.RemoveMessagesAsync(topic);
return removedDeadLetter.Concat(removedBuffer).ToList();
}
}

View File

@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// MessageRegistryQueue.cs
using System.Collections.Concurrent;
using Microsoft.AutoGen.Contracts;
using Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
using Microsoft.Extensions.Logging;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc;
public sealed class MessageRegistryQueue
{
private ConcurrentDictionary<string, List<CloudEvent>> _queue = new();
private readonly int _maxMessageSize;
private readonly int _maxQueueSize;
private readonly Dictionary<DateTime, string> _timestamps = new();
private int _currentSize;
private readonly IPersistentState<MessageRegistryState> _state;
private readonly ILogger _logger;
private readonly StateManager _stateManager;
private readonly MessageRegistryGrain.QueueType _queueType;
internal MessageRegistryQueue(MessageRegistryGrain.QueueType queueType,
IPersistentState<MessageRegistryState> state,
StateManager stateManager,
ILogger logger,
int maxMessageSize,
int maxQueueSize)
{
if (state.State == null)
{
state.State = new MessageRegistryState();
}
_queueType = queueType;
_state = state;
// use the queueType to get the correct queue from state.State.
_queue = GetQueue();
_stateManager = stateManager;
_logger = logger;
_maxMessageSize = maxMessageSize;
_maxQueueSize = maxQueueSize;
}
public async Task AddMessageAsync(string topic, CloudEvent message)
{
var size = message.CalculateSize();
if (size > _maxMessageSize)
{
_logger.LogWarning("Message size {Size} for topic {Topic} in queue {Name} exceeds the maximum message size {Max}.",
size, topic, _queueType.ToString(), _maxMessageSize);
return;
}
if (_currentSize + size > _maxQueueSize)
{
while (_currentSize + size > _maxQueueSize && _timestamps.Count > 0)
{
var oldest = _timestamps.OrderBy(x => x.Key).First();
if (await RemoveOldestMessage(oldest.Value))
{
_timestamps.Remove(oldest.Key);
}
}
}
await AddOrUpdate(topic, message);
_currentSize += size;
}
public async Task<List<CloudEvent>> RemoveMessagesAsync(string topic)
{
var removed = new List<CloudEvent>();
var queue = GetQueue();
if (queue.Remove(topic, out var events))
{
removed.AddRange(events);
var total = 0;
foreach (var e in events) { total += e.CalculateSize(); }
_currentSize -= total;
}
// Remove timestamps that refer to this topic
var toRemove = _timestamps.Where(x => x.Value == topic).Select(x => x.Key).ToList();
foreach (var t in toRemove) { _timestamps.Remove(t); }
await _stateManager.WriteStateAsync().ConfigureAwait(true);
return removed;
}
public async Task<bool> RemoveMessageAsync(string topic, CloudEvent message)
{
var queue = GetQueue();
if (queue.TryGetValue(topic, out var events) && events.Remove(message))
{
_currentSize -= message.CalculateSize();
await _stateManager.WriteStateAsync().ConfigureAwait(true);
return true;
}
return false;
}
private async Task<bool> RemoveOldestMessage(string topic)
{
var queue = GetQueue();
if (queue.TryGetValue(topic, out var events) && events != null && events.Count > 0)
{
var oldestEvent = events[0];
events.RemoveAt(0);
_currentSize -= oldestEvent.CalculateSize();
_timestamps.Remove(_timestamps.OrderBy(x => x.Key).First().Key);
queue[topic] = events;
await _stateManager.WriteStateAsync().ConfigureAwait(true);
return true;
}
return false;
}
private async Task AddOrUpdate(string topic, CloudEvent message)
{
var queue = GetQueue();
var list = queue.GetOrAdd(topic, _ => new());
list.Add(message);
queue.AddOrUpdate(topic, list, (_, _) => list);
await _stateManager.WriteStateAsync().ConfigureAwait(true);
_timestamps.Add(DateTime.UtcNow, topic);
}
private ConcurrentDictionary<string, List<CloudEvent>> GetQueue()
{
return _queueType switch
{
MessageRegistryGrain.QueueType.DeadLetterQueue => _state.State.DeadLetterQueue,
MessageRegistryGrain.QueueType.EventBuffer => _state.State.EventBuffer,
_ => throw new ArgumentException($"Invalid queue type: {_queueType}.")
};
}
public async Task RemoveMessageAfterDelayAsync(string topic, CloudEvent message, int delay)
{
await Task.Delay(delay);
await RemoveMessageAsync(topic, message);
_currentSize -= message.CalculateSize();
}
}

View File

@ -277,6 +277,7 @@ internal sealed class RegistryGrain([PersistentState("state", "AgentRegistryStor
}
return new(subscriptions);
}
private sealed class WorkerState
{
public HashSet<string> SupportedTypes { get; set; } = [];

View File

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// StateManager.cs
using Orleans.Core;
namespace Microsoft.AutoGen.RuntimeGateway.Grpc;
/// <summary>
/// A helper class which wraps a grain state instance and ensures that only a single write operation is outstanding at any moment in time.
/// </summary>
/// <param name="state">The grain state.</param>
internal sealed class StateManager(IStorage state)
{
/// <summary>
/// Allows state writing to happen in the background.
/// </summary>
private Task? _pendingOperation;
// When reentrant grain is doing WriteStateAsync, etag violations are possible due to concurrent writes.
// The solution is to serialize and batch writes, and make sure only a single write is outstanding at any moment in time.
public async ValueTask WriteStateAsync()
{
await PerformOperationAsync(static state => state.WriteStateAsync());
}
public async ValueTask ClearStateAsync()
{
await PerformOperationAsync(static state => state.ClearStateAsync());
}
public async ValueTask PerformOperationAsync(Func<IStorage, Task> performOperation)
{
if (_pendingOperation is Task currentWriteStateOperation)
{
// await the outstanding write, but ignore it since it doesn't include our changes
await currentWriteStateOperation.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ContinueOnCapturedContext);
if (_pendingOperation == currentWriteStateOperation)
{
// only null out the outstanding operation if it's the same one as the one we awaited, otherwise
// another request might have already done so.
_pendingOperation = null;
}
}
Task operation;
if (_pendingOperation is null)
{
// If after the initial write is completed, no other request initiated a new write operation, do it now.
operation = performOperation(state);
_pendingOperation = operation;
}
else
{
// If there were many requests enqueued to persist state, there is no reason to enqueue a new write
// operation for each, since any write (after the initial one that we already awaited) will have cumulative
// changes including the one requested by our caller. Just await the new outstanding write.
operation = _pendingOperation;
}
try
{
await operation;
}
finally
{
if (_pendingOperation == operation)
{
// only null out the outstanding operation if it's the same one as the one we awaited, otherwise
// another request might have already done so.
_pendingOperation = null;
}
}
}
}

View File

@ -17,6 +17,11 @@ public struct TypeSubscriptionSurrogate
public sealed class TypeSubscriptionSurrogateConverter :
IConverter<TypeSubscription, TypeSubscriptionSurrogate>
{
/// <summary>
/// Converts from the surrogate to the original type.
/// </summary>
/// <param name="surrogate">The surrogate to convert from.</param>
/// <returns>The original type.</returns>
public TypeSubscription ConvertFromSurrogate(
in TypeSubscriptionSurrogate surrogate) =>
new TypeSubscription
@ -25,6 +30,11 @@ public sealed class TypeSubscriptionSurrogateConverter :
AgentType = surrogate.AgentType
};
/// <summary>
/// Converts from the original type to the surrogate.
/// </summary>
/// <param name="value">The original type to convert from.</param>
/// <returns>The surrogate type.</returns>
public TypeSubscriptionSurrogate ConvertToSurrogate(
in TypeSubscription value) =>
new TypeSubscriptionSurrogate

View File

@ -12,6 +12,12 @@
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
@ -31,6 +37,12 @@
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
@ -57,6 +69,12 @@
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
@ -78,6 +96,12 @@
"ImageUri": "https://example.com/image.png",
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
@ -104,6 +128,12 @@
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
},
@ -113,6 +143,12 @@
"ImageUri": "https://example.com/image.png",
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}

View File

@ -0,0 +1,260 @@
[
{
"OriginalMessage": "TextMessage(system, You are a helpful AI assistant, )",
"ConvertedMessages": [
{
"Name": null,
"Role": "system",
"Content": [
{
"Kind": 0,
"Text": "You are a helpful AI assistant",
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
]
}
]
},
{
"OriginalMessage": "TextMessage(user, Hello, user)",
"ConvertedMessages": [
{
"Role": "user",
"Content": [
{
"Kind": 0,
"Text": "Hello",
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
],
"Name": "user",
"MultiModaItem": [
{
"Type": "Text",
"Text": "Hello"
}
]
}
]
},
{
"OriginalMessage": "TextMessage(assistant, How can I help you?, assistant)",
"ConvertedMessages": [
{
"Role": "assistant",
"Content": [
{
"Kind": 0,
"Text": "How can I help you?",
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
],
"Name": "assistant",
"TooCall": []
}
]
},
{
"OriginalMessage": "ImageMessage(user, https://example.com/image.png, user)",
"ConvertedMessages": [
{
"Role": "user",
"Content": [
{
"Kind": 2,
"Text": null,
"ImageUri": "https://example.com/image.png",
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
],
"Name": "user",
"MultiModaItem": [
{
"Type": "Image",
"ImageUrl": "https://example.com/image.png"
}
]
}
]
},
{
"OriginalMessage": "MultiModalMessage(assistant, user)\n\tTextMessage(user, Hello, user)\n\tImageMessage(user, https://example.com/image.png, user)",
"ConvertedMessages": [
{
"Role": "user",
"Content": [
{
"Kind": 0,
"Text": "Hello",
"ImageUri": null,
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
},
{
"Kind": 2,
"Text": null,
"ImageUri": "https://example.com/image.png",
"ImageBytes": null,
"ImageBytesMediaType": null,
"InputAudioBytes": null,
"InputAudioFormat": null,
"FileId": null,
"FileBytes": null,
"FileBytesMediaType": null,
"Filename": null,
"ImageDetailLevel": null,
"Refusal": null
}
],
"Name": "user",
"MultiModaItem": [
{
"Type": "Text",
"Text": "Hello"
},
{
"Type": "Image",
"ImageUrl": "https://example.com/image.png"
}
]
}
]
},
{
"OriginalMessage": "ToolCallMessage(assistant)\n\tToolCall(test, test, )",
"ConvertedMessages": [
{
"Role": "assistant",
"Content": [],
"Name": null,
"TooCall": [
{
"Type": "Function",
"Name": "test",
"Arguments": "dGVzdA==",
"Id": "test"
}
]
}
]
},
{
"OriginalMessage": "ToolCallResultMessage(user)\n\tToolCall(test, test, result)",
"ConvertedMessages": [
{
"Role": "tool",
"Content": "result",
"ToolCallId": "test"
}
]
},
{
"OriginalMessage": "ToolCallResultMessage(user)\n\tToolCall(result, test, test)\n\tToolCall(result, test, test)",
"ConvertedMessages": [
{
"Role": "tool",
"Content": "test",
"ToolCallId": "result_0"
},
{
"Role": "tool",
"Content": "test",
"ToolCallId": "result_1"
}
]
},
{
"OriginalMessage": "ToolCallMessage(assistant)\n\tToolCall(test, test, )\n\tToolCall(test, test, )",
"ConvertedMessages": [
{
"Role": "assistant",
"Content": [],
"Name": null,
"TooCall": [
{
"Type": "Function",
"Name": "test",
"Arguments": "dGVzdA==",
"Id": "test_0"
},
{
"Type": "Function",
"Name": "test",
"Arguments": "dGVzdA==",
"Id": "test_1"
}
]
}
]
},
{
"OriginalMessage": "AggregateMessage(assistant)\n\tToolCallMessage(assistant)\n\tToolCall(test, test, )\n\tToolCallResultMessage(assistant)\n\tToolCall(test, test, result)",
"ConvertedMessages": [
{
"Role": "assistant",
"Content": [],
"Name": null,
"TooCall": [
{
"Type": "Function",
"Name": "test",
"Arguments": "dGVzdA==",
"Id": "test"
}
]
},
{
"Role": "tool",
"Content": "result",
"ToolCallId": "test"
}
]
}
]

View File

@ -37,25 +37,25 @@ public partial class SemanticKernelAgentTest
.AddAzureOpenAIChatCompletion(deploymentName, endpoint, key);
var kernel = builder.Build();
kernel.GetRequiredService<IChatCompletionService>();
var skAgent = new SemanticKernelAgent(kernel, "assistant");
var chatMessageContent = MessageEnvelope.Create(new ChatMessageContent(AuthorRole.Assistant, "Hello"));
var reply = await skAgent.SendAsync(chatMessageContent);
await TestBasicConversationAsync(skAgent);
}
reply.Should().BeOfType<MessageEnvelope<ChatMessageContent>>();
reply.As<MessageEnvelope<ChatMessageContent>>().From.Should().Be("assistant");
[ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOY_NAME")]
public async Task BasicConversationTestWithKeyedServiceAsync()
{
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable.");
var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new Exception("Please set AZURE_OPENAI_API_KEY environment variable.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOY_NAME") ?? throw new Exception("Please set AZURE_OPENAI_DEPLOY_NAME environment variable.");
var modelServiceId = "my-service-id";
var builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(deploymentName, endpoint, key, modelServiceId);
// test streaming
var streamingReply = skAgent.GenerateStreamingReplyAsync(new[] { chatMessageContent });
var kernel = builder.Build();
var skAgent = new SemanticKernelAgent(kernel, "assistant", modelServiceId: modelServiceId);
await foreach (var streamingMessage in streamingReply)
{
streamingMessage.Should().BeOfType<MessageEnvelope<StreamingChatMessageContent>>();
streamingMessage.As<MessageEnvelope<StreamingChatMessageContent>>().From.Should().Be("assistant");
}
await TestBasicConversationAsync(skAgent);
}
[ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOY_NAME")]
@ -241,4 +241,22 @@ public partial class SemanticKernelAgentTest
reply.GetContent()!.ToLower().Should().Contain("seattle");
reply.GetContent()!.ToLower().Should().Contain("sunny");
}
private static async Task TestBasicConversationAsync(SemanticKernelAgent agent)
{
var chatMessageContent = MessageEnvelope.Create(new ChatMessageContent(AuthorRole.Assistant, "Hello"));
var reply = await agent.SendAsync(chatMessageContent);
reply.Should().BeOfType<MessageEnvelope<ChatMessageContent>>();
reply.As<MessageEnvelope<ChatMessageContent>>().From.Should().Be("assistant");
// test streaming
var streamingReply = agent.GenerateStreamingReplyAsync(new[] { chatMessageContent });
await foreach (var streamingMessage in streamingReply)
{
streamingMessage.Should().BeOfType<MessageEnvelope<StreamingChatMessageContent>>();
streamingMessage.As<MessageEnvelope<StreamingChatMessageContent>>().From.Should().Be("assistant");
}
}
}

View File

@ -60,7 +60,7 @@ public class FunctionTests
GetWeatherAsyncStatic,
];
var functionContracts = availableDelegates.Select(function => (FunctionContract)AIFunctionFactory.Create(function).Metadata).ToList();
var functionContracts = availableDelegates.Select(function => (FunctionContract)AIFunctionFactory.Create(function)).ToList();
// Verify the function contracts
functionContracts.Should().HaveCount(4);

View File

@ -18,7 +18,6 @@ using AutoGen.Mistral.Extension;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;
using Azure.AI.Inference;
using Azure.AI.OpenAI;
using FluentAssertions;
using Moq;
using OpenAI;
@ -217,21 +216,6 @@ public class RolePlayOrchestratorTests
speaker.Should().Be(bob);
}
[ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOY_NAME")]
public async Task GPT_3_5_CoderReviewerRunnerTestAsync()
{
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable.");
var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new Exception("Please set AZURE_OPENAI_API_KEY environment variable.");
var deployName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOY_NAME") ?? throw new Exception("Please set AZURE_OPENAI_DEPLOY_NAME environment variable.");
var openaiClient = new AzureOpenAIClient(new Uri(endpoint), new System.ClientModel.ApiKeyCredential(key));
var openAIChatAgent = new OpenAIChatAgent(
chatClient: openaiClient.GetChatClient(deployName),
name: "assistant")
.RegisterMessageConnector();
await CoderReviewerRunnerTestAsync(openAIChatAgent);
}
[ApiKeyFact("OPENAI_API_KEY")]
public async Task GPT_4o_CoderReviewerRunnerTestAsync()
{

View File

@ -20,11 +20,11 @@ public partial class SingleAgentTest
_output = output;
}
private ILLMConfig CreateAzureOpenAIGPT35TurboConfig()
private ILLMConfig CreateAzureOpenAIGPT4oMiniConfig()
{
var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new ArgumentException("AZURE_OPENAI_API_KEY is not set");
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new ArgumentException("AZURE_OPENAI_ENDPOINT is not set");
var deployName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOY_NAME") ?? throw new ArgumentException("AZURE_OPENAI_DEPLOY_NAME is not set");
var deployName = "gpt-4o-mini";
return new AzureOpenAIConfig(endpoint, deployName, key);
}
@ -37,7 +37,7 @@ public partial class SingleAgentTest
[ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOY_NAME")]
public async Task AssistantAgentFunctionCallTestAsync()
{
var config = this.CreateAzureOpenAIGPT35TurboConfig();
var config = this.CreateAzureOpenAIGPT4oMiniConfig();
var llmConfig = new ConversableAgentConfig
{
@ -77,7 +77,7 @@ public partial class SingleAgentTest
[ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOY_NAME")]
public async Task AssistantAgentFunctionCallSelfExecutionTestAsync()
{
var config = this.CreateAzureOpenAIGPT35TurboConfig();
var config = this.CreateAzureOpenAIGPT4oMiniConfig();
var llmConfig = new ConversableAgentConfig
{
FunctionContracts = new[]

View File

@ -32,7 +32,7 @@ public partial class TwoAgentTest
{
var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new ArgumentException("AZURE_OPENAI_API_KEY is not set");
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new ArgumentException("AZURE_OPENAI_ENDPOINT is not set");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOY_NAME") ?? throw new ArgumentException("AZURE_OPENAI_DEPLOY_NAME is not set");
var deploymentName = "gpt-4o-mini";
var config = new AzureOpenAIConfig(endpoint, deploymentName, key);
var assistant = new AssistantAgent(

View File

@ -1,14 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// AgentChatSmokeTest.cs
using System.Text.Json;
using Microsoft.AutoGen.AgentChat.Abstractions;
using Microsoft.AutoGen.AgentChat.Agents;
using Microsoft.AutoGen.AgentChat.GroupChat;
using Microsoft.AutoGen.AgentChat.State;
using Microsoft.AutoGen.AgentChat.Terminations;
using Microsoft.AutoGen.Contracts;
using Xunit;
namespace Microsoft.AutoGen.AgentChat.Tests;
[Trait("Category", "UnitV2")]
public class AgentChatSmokeTest
{
public class SpeakMessageAgent : ChatAgentBase
@ -38,7 +42,7 @@ public class AgentChatSmokeTest
}
}
public class TerminatingAgent : ChatAgentBase
public class TerminatingAgent : ChatAgentBase, ISaveState
{
public List<ChatMessage>? IncomingMessages { get; private set; }
@ -82,6 +86,41 @@ public class AgentChatSmokeTest
return ValueTask.CompletedTask;
}
public class State : BaseState
{
public required List<ChatMessage> IncomingMessages { get; set; }
}
ValueTask<JsonElement> ISaveState.SaveStateAsync()
{
SerializedState serializedState = SerializedState.Create(new State
{
IncomingMessages = this.IncomingMessages ?? new List<ChatMessage>()
});
return ValueTask.FromResult(serializedState.AsJson());
}
ValueTask ISaveState.LoadStateAsync(JsonElement state)
{
State parsedState = new SerializedState(state).As<State>();
this.IncomingMessages = [.. parsedState.IncomingMessages];
return ValueTask.CompletedTask;
}
}
private ValueTask<TaskResult> RunChatAsync(TerminatingAgent terminatingAgent, out ITeam chat)
{
chat = new RoundRobinGroupChat(
[
new SpeakMessageAgent("Speak", "Speak", "Hello"),
terminatingAgent,
],
terminationCondition: new StopMessageTermination());
return chat.RunAsync("");
}
[Fact]
@ -89,18 +128,66 @@ public class AgentChatSmokeTest
{
TerminatingAgent terminatingAgent = new("Terminate", "Terminate");
ITeam chat = new RoundRobinGroupChat(
[
new SpeakMessageAgent("Speak", "Speak", "Hello"),
terminatingAgent
],
terminationCondition: new StopMessageTermination());
TaskResult result = await chat.RunAsync("");
TaskResult result = await this.RunChatAsync(terminatingAgent, out _);
Assert.Equal(3, result.Messages.Count);
Assert.Equal("", Assert.IsType<TextMessage>(result.Messages[0]).Content);
Assert.Equal("Hello", Assert.IsType<TextMessage>(result.Messages[1]).Content);
Assert.Equal("Terminating; got: Hello", Assert.IsType<StopMessage>(result.Messages[2]).Content);
}
[Fact]
public async Task Test_RoundRobin_SpeakTerminateReset()
{
TerminatingAgent terminatingAgent = new("Terminate", "Terminate");
await this.RunChatAsync(terminatingAgent, out ITeam chat);
Assert.NotNull(terminatingAgent.IncomingMessages);
await chat.ResetAsync();
Assert.Null(terminatingAgent.IncomingMessages);
}
[Fact]
public async Task Test_RoundRobin_SaveLoadRun()
{
TerminatingAgent t1 = new("Terminate1", "Terminate"), t2 = new("Terminate2", "Terminate");
SpeakMessageAgent s1 = new("Speak1", "Speak", "Hello"), s2 = new("Speak2", "Speak", "World");
ITeam chat = new RoundRobinGroupChat(
[s1, t1, s2, t2],
terminationCondition: new StopMessageTermination());
TaskResult result = await chat.RunAsync("1");
Assert.Equal(3, result.Messages.Count);
Assert.Equal("1", Assert.IsType<TextMessage>(result.Messages[0]).Content);
Assert.Equal("Hello", Assert.IsType<TextMessage>(result.Messages[1]).Content);
Assert.Equal("Terminating; got: Hello", Assert.IsType<StopMessage>(result.Messages[2]).Content);
// Save state
JsonElement state = await chat.SaveStateAsync();
// Reset chat
await chat.ResetAsync();
Assert.Null(t1.IncomingMessages);
// Load state
await chat.LoadStateAsync(state);
Assert.NotNull(t1.IncomingMessages);
// Check that we resume the conversation in the right place
TaskResult result2 = await chat.RunAsync("2");
Assert.Equal(3, result.Messages.Count);
Assert.Equal("2", Assert.IsType<TextMessage>(result2.Messages[0]).Content);
Assert.Equal("World", Assert.IsType<TextMessage>(result2.Messages[1]).Content);
Assert.Equal("Terminating; got: World", Assert.IsType<StopMessage>(result2.Messages[2]).Content);
}
}

View File

@ -0,0 +1,194 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// LifecycleObjectTests.cs
using FluentAssertions;
using Microsoft.AutoGen.AgentChat.GroupChat;
using Xunit;
namespace Microsoft.AutoGen.AgentChat.Tests;
internal sealed class LifecycleObjectFixture : LifecycleObject
{
public enum LifecycleState
{
Deinitialized,
Initialized
}
public LifecycleState State { get; private set; }
public Func<ValueTask> DeinitializeOverride { get; set; } = () => ValueTask.CompletedTask;
public Func<ValueTask> InitializeOverride { get; set; } = () => ValueTask.CompletedTask;
public Action InitializeErrorOverride { get; set; }
public Action DeinitializeErrorOverride { get; set; }
private int initializeCallCount;
private int deinitializeCallCount;
private int initializeErrorCount;
private int deinitializeErrorCount;
public int InitializeCallCount => this.initializeCallCount;
public int DeinitializeCallCount => this.deinitializeCallCount;
public int InitializeErrorCount => this.initializeErrorCount;
public int DeinitializeErrorCount => this.deinitializeErrorCount;
public LifecycleObjectFixture()
{
this.State = LifecycleState.Deinitialized;
this.InitializeErrorOverride = base.OnInitializeError;
this.DeinitializeErrorOverride = base.OnDeinitializeError;
}
protected override void OnInitializeError()
{
Interlocked.Increment(ref this.initializeErrorCount);
this.InitializeErrorOverride();
}
protected override void OnDeinitializeError()
{
Interlocked.Increment(ref this.deinitializeErrorCount);
this.DeinitializeErrorOverride();
}
protected sealed override ValueTask DeinitializeCore()
{
Interlocked.Increment(ref this.deinitializeCallCount);
this.State = LifecycleState.Deinitialized;
return DeinitializeOverride();
}
protected sealed override ValueTask InitializeCore()
{
Interlocked.Increment(ref this.initializeCallCount);
this.State = LifecycleState.Initialized;
return InitializeOverride();
}
}
[Trait("Category", "UnitV2")]
public class LifecycleObjectTests
{
/*
We should be testing the following conditions:
- SmokeTest: Happy path: Initialize, Deinitialize, Initialize, Deinitialize, validate states and call counts
- Error handling: Initialize, Initialize; Deinitialize; Initialize, Deinitialize, Deinitialize
*/
[Fact]
public async Task InitializeAndDeinitialize_SucceedsTwice()
{
// Arrange
LifecycleObjectFixture fixture = new();
// Validate preconditions
fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Deinitialized, "LifecycleObject should be in Deinitialized state initially");
fixture.InitializeCallCount.Should().Be(0, "Initialize should not have been called yet");
fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called yet");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
// Act
await fixture.InitializeAsync();
// Validate postconditions 1
fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Initialized, "LifecycleObject should be in Initialized state after Initialize");
fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once");
fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called yet");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
// Act 2
await fixture.DeinitializeAsync();
// Validate postconditions 2
fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Deinitialized, "LifecycleObject should be in Deinitialized state after Deinitialize");
fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once");
fixture.DeinitializeCallCount.Should().Be(1, "Deinitialize should have been called once");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
// Act 3
await fixture.InitializeAsync();
// Validate postconditions 3
fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Initialized, "LifecycleObject should be in Initialized state after Initialize");
fixture.InitializeCallCount.Should().Be(2, "Initialize should have been called twice");
fixture.DeinitializeCallCount.Should().Be(1, "Deinitialize should have been called once");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
// Act 4
await fixture.DeinitializeAsync();
// Validate postconditions 4
fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Deinitialized, "LifecycleObject should be in Deinitialized state after Deinitialize");
fixture.InitializeCallCount.Should().Be(2, "Initialize should have been called twice");
fixture.DeinitializeCallCount.Should().Be(2, "Deinitialize should have been called twice");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
}
[Fact]
public async Task Initialize_FailsWhenInitialized()
{
// Testing two things: We should expect InvalidOperationException by default, and that we called into the override
// Arrange
LifecycleObjectFixture fixture = new();
await fixture.InitializeAsync();
// Act
Func<Task> secondInitialization = async () => await fixture.InitializeAsync();
// Assert
await secondInitialization.Should().ThrowAsync<InvalidOperationException>("LifecycleObject.InitializeAsync should throw InvalidOperationException when initialized");
fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once successfully");
fixture.InitializeErrorCount.Should().Be(1, "there should be one initialization error");
fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called yet");
fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
}
[Fact]
public async Task Deinitialize_FailsWhenNotInitialized()
{
// Arrange
LifecycleObjectFixture fixture = new();
// Act
Func<Task> deinitialization = async () => await fixture.DeinitializeAsync();
// Assert
await deinitialization.Should().ThrowAsync<InvalidOperationException>("LifecycleObject.DeinitializeAsync should throw InvalidOperationException when not initialized");
fixture.InitializeCallCount.Should().Be(0, "Initialize should not have been called yet");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called successfully yet");
fixture.DeinitializeErrorCount.Should().Be(1, "there should be one deinitialization error");
// Act 2
await fixture.InitializeAsync();
await fixture.DeinitializeAsync();
Func<Task> secondDeinitialization = async () => await fixture.DeinitializeAsync();
// Assert 2
await secondDeinitialization.Should().ThrowAsync<InvalidOperationException>("LifecycleObject.DeinitializeAsync should throw InvalidOperationException when not initialized");
fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once successfully");
fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
fixture.DeinitializeCallCount.Should().Be(1, "Deinitialize should have been called successfully once");
fixture.DeinitializeErrorCount.Should().Be(2, "there should be two deinitialization errors");
}
}

View File

@ -7,6 +7,12 @@
<IsTestProject>True</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AutoGen\AgentChat\Microsoft.AutoGen.AgentChat.csproj" />
<PackageReference Include="Microsoft.Extensions.Hosting" />

View File

@ -0,0 +1,265 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// RunContextStackTests.cs
using FluentAssertions;
using Microsoft.AutoGen.AgentChat.GroupChat;
using Moq;
using Xunit;
namespace Microsoft.AutoGen.AgentChat.Tests;
[Trait("Category", "UnitV2")]
public class RunContextStackTests
{
public static IRunContextLayer CreateLayer(Action<Mock<IRunContextLayer>>? setupAction = null)
{
Mock<IRunContextLayer> layer = new();
if (setupAction != null)
{
setupAction(layer);
}
else
{
layer.Setup(l => l.InitializeAsync()).Returns(ValueTask.CompletedTask);
layer.Setup(l => l.DeinitializeAsync()).Returns(ValueTask.CompletedTask);
}
return layer.Object;
}
[Fact]
public async Task Initialize_SucceedsWithNoLayers()
{
// Arrange
RunContextStack stack = new RunContextStack();
// Act
Func<Task> func = async () => await stack.InitializeAsync();
// Assert
await func.Should().NotThrowAsync("RunContextStack should work without context frames");
}
[Fact]
public async Task Deinitialize_SucceedsWithNoLayers()
{
// Arrange
RunContextStack stack = new RunContextStack();
await stack.InitializeAsync();
// Act
Func<Task> func = async () => await stack.DeinitializeAsync();
// Assert
await func.Should().NotThrowAsync("RunContextStack should work without context frames");
}
[Fact]
public async Task PushLayer_FailsWhenInitialized()
{
// Arrange
RunContextStack stack = new RunContextStack();
await stack.InitializeAsync();
// Act
Action pushLayerAction = () => stack.PushLayer(CreateLayer());
// Assert
pushLayerAction.Should().Throw<InvalidOperationException>("RunContextStack should not allow pushing layers when initialized");
}
[Fact]
public async Task PopLayer_FailsWhenInitialized()
{
// Arrange
RunContextStack stack = new RunContextStack();
await stack.InitializeAsync();
// Act
Action popLayerAction = stack.PopLayer;
// Assert
popLayerAction.Should().Throw<InvalidOperationException>("RunContextStack should not allow popping layers when initialized");
}
[Fact]
public Task InitializeDeinitialize_ShouldInvokeLayersInOrder_WhenPushed()
{
return PrepareAndRun_LayerOrderTest(Arrange);
static RunContextStack Arrange(IEnumerable<IRunContextLayer> layers)
{
RunContextStack stack = new RunContextStack();
foreach (IRunContextLayer layer in layers)
{
stack.PushLayer(layer);
}
return stack;
}
}
[Fact]
public Task InitializeDeinitialize_ShouldInvokeLayersInOrder_WhenConstructed()
{
return PrepareAndRun_LayerOrderTest(Arrange);
static RunContextStack Arrange(IEnumerable<IRunContextLayer> layers)
{
return new RunContextStack([.. layers]);
}
}
private async Task PrepareAndRun_LayerOrderTest(Func<IEnumerable<IRunContextLayer>, RunContextStack> arrangeStack)
{
bool bottomLayerInit = false;
bool bottomLayerDeinit = false;
bool topLayerInit = false;
bool topLayerDeinit = false;
// Arrange
IRunContextLayer topLayer = CreateLayer(mock =>
{
mock.Setup(l => l.InitializeAsync()).Callback(
() =>
{
topLayerInit.Should().BeFalse("Top Layer should not have been initialized yet");
bottomLayerInit.Should().BeFalse("Bottom Layer should not have been initialized yet");
topLayerInit = true;
}
).Returns(ValueTask.CompletedTask).Verifiable();
mock.Setup(l => l.DeinitializeAsync()).Callback(
() =>
{
topLayerInit.Should().BeTrue("Top Layer should have been initialized");
bottomLayerInit.Should().BeTrue("Bottom Layer should have been initialized");
bottomLayerDeinit.Should().BeTrue("Bottom Layer should be deinitialized before Top Layer");
topLayerDeinit.Should().BeFalse("Top Layer should not have been deinitialized yet");
topLayerDeinit = true;
}).Returns(ValueTask.CompletedTask).Verifiable();
});
IRunContextLayer bottomLayer = CreateLayer(mock =>
{
mock.Setup(l => l.InitializeAsync()).Callback(
() =>
{
topLayerInit.Should().BeTrue("Top Layer should have been initialized before Bottom Layer");
bottomLayerInit.Should().BeFalse("Bottom Layer should not have been initialized yet");
bottomLayerInit = true;
}
).Returns(ValueTask.CompletedTask).Verifiable();
mock.Setup(l => l.DeinitializeAsync()).Callback(
() =>
{
topLayerInit.Should().BeTrue("Top Layer should have been initialized");
bottomLayerInit.Should().BeTrue("Bottom Layer should have been initialized");
bottomLayerDeinit.Should().BeFalse("Bottom Layer should not have been deinitialized yet");
topLayerDeinit.Should().BeFalse("Top Layer should not have been deinitialized yet");
bottomLayerDeinit = true;
}).Returns(ValueTask.CompletedTask).Verifiable();
});
RunContextStack stack = arrangeStack([bottomLayer, topLayer]);
// Act
await stack.InitializeAsync();
// Assert
Mock.Get(topLayer).Verify(l => l.InitializeAsync(), Times.Once);
Mock.Get(bottomLayer).Verify(l => l.InitializeAsync(), Times.Once);
bottomLayerInit.Should().BeTrue("Top Layer should have been initialized");
topLayerInit.Should().BeTrue("Bottom Layer should have been initialized");
// Act 2
await stack.DeinitializeAsync();
// Assert 2
Mock.Get(bottomLayer).Verify(l => l.DeinitializeAsync(), Times.Once);
Mock.Get(topLayer).Verify(l => l.DeinitializeAsync(), Times.Once);
topLayerDeinit.Should().BeTrue("Bottom Layer should have been deinitialized");
bottomLayerDeinit.Should().BeTrue("Top Layer should have been deinitialized");
}
[Fact]
public async Task CreateOverrides_GetsInvokedOnError()
{
int initializeErrors = 0;
int deinitializeErrors = 0;
// Arrange
IRunContextLayer overrides = RunContextStack.OverrideErrors(
initializeError: () => initializeErrors++,
deinitializeError: () => deinitializeErrors++);
RunContextStack stack = new RunContextStack(overrides);
// Act
Func<Task> deinitializeAction = async () => await stack.DeinitializeAsync();
// Assert
// The first Deinitialize should throw because we only override after the top layer it initialized
await deinitializeAction.Should().ThrowAsync<InvalidOperationException>("Deinitialize should throw an exception");
// Act 2
await stack.InitializeAsync();
Func<Task> initializeAgainAction = async () => await stack.InitializeAsync();
// Assert 2
// The second Initialize should not throw, because the overrides should be applied
await initializeAgainAction.Should().NotThrowAsync("Initialize should not throw an exception");
initializeErrors.Should().Be(1, "There should be one initialization error");
deinitializeErrors.Should().Be(0, "There should not have been an overriden invocation of a deinitialize error.");
}
[Fact]
public async Task Enter_DisposableWorksIdempotently()
{
int initializeCount = 0;
int deinitializeCount = 0;
// Arrange
IRunContextLayer layer = CreateLayer(mock =>
{
mock.Setup(l => l.InitializeAsync()).Callback(() => initializeCount++).Returns(ValueTask.CompletedTask);
mock.Setup(l => l.DeinitializeAsync()).Callback(() => deinitializeCount++).Returns(ValueTask.CompletedTask);
});
RunContextStack stack = new RunContextStack(layer);
// Act
IAsyncDisposable exitDisposable = await stack.Enter();
// Assert
initializeCount.Should().Be(1, "Layer should have been initialized once");
deinitializeCount.Should().Be(0, "Layer should not have been deinitialized yet");
// Act 2
await exitDisposable.DisposeAsync();
// Assert 2
initializeCount.Should().Be(1, "Layer should have been initialized once");
deinitializeCount.Should().Be(1, "Layer should have been deinitialized once");
// Act 3
Func<Task> disposeAgain = async () => await exitDisposable.DisposeAsync();
// Assert 3
await disposeAgain.Should().NotThrowAsync("Dispose should be idempotent");
initializeCount.Should().Be(1, "Layer should have been initialized once");
deinitializeCount.Should().Be(1, "Layer should have been deinitialized once");
}
}

View File

@ -0,0 +1,476 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// TerminationConditionTests.cs
using FluentAssertions;
using Microsoft.AutoGen.AgentChat.Abstractions;
using Microsoft.AutoGen.AgentChat.Terminations;
using Microsoft.Extensions.AI;
using Xunit;
namespace Microsoft.AutoGen.AgentChat.Tests;
[Trait("Category", "UnitV2")]
public static class TerminationExtensions
{
public static async Task InvokeExpectingNullAsync<TTermination>(this TTermination termination, IList<AgentMessage> messages, bool reset = true)
where TTermination : ITerminationCondition
{
(await termination.CheckAndUpdateAsync(messages)).Should().BeNull();
termination.IsTerminated.Should().BeFalse();
if (reset)
{
termination.Reset();
}
}
private static readonly HashSet<string> AnonymousTerminationConditions = ["CombinerCondition", nameof(ITerminationCondition)];
public static async Task InvokeExpectingStopAsync<TTermination>(this TTermination termination, IList<AgentMessage> messages, bool reset = true)
where TTermination : ITerminationCondition
{
StopMessage? stopMessage = await termination.CheckAndUpdateAsync(messages);
stopMessage.Should().NotBeNull();
string name = typeof(TTermination).Name;
if (!AnonymousTerminationConditions.Contains(name))
{
stopMessage!.Source.Should().Be(typeof(TTermination).Name);
}
termination.IsTerminated.Should().BeTrue();
if (reset)
{
termination.Reset();
}
}
public static async Task InvokeExpectingFailureAsync<TTermination>(this TTermination termination, IList<AgentMessage> messages, bool reset = true)
where TTermination : ITerminationCondition
{
Func<Task> failureAction = () => termination.CheckAndUpdateAsync(messages).AsTask();
await failureAction.Should().ThrowAsync<TerminatedException>();
termination.IsTerminated.Should().BeTrue();
if (reset)
{
termination.Reset();
}
}
}
public class TerminationConditionTests
{
[Fact]
public async Task Test_HandoffTermination()
{
HandoffTermination termination = new("target");
termination.IsTerminated.Should().BeFalse();
TextMessage textMessage = new() { Content = "Hello", Source = "user" };
HandoffMessage targetHandoffMessage = new() { Target = "target", Source = "user", Context = "Hello" };
HandoffMessage otherHandoffMessage = new() { Target = "another", Source = "user", Context = "Hello" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([textMessage]);
await termination.InvokeExpectingStopAsync([targetHandoffMessage]);
await termination.InvokeExpectingNullAsync([otherHandoffMessage]);
await termination.InvokeExpectingStopAsync([textMessage, targetHandoffMessage], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task StopMessageTermination()
{
StopMessageTermination termination = new();
termination.IsTerminated.Should().BeFalse();
TextMessage textMessage = new() { Content = "Hello", Source = "user" };
TextMessage otherMessage = new() { Content = "World", Source = "aser" };
StopMessage stopMessage = new() { Content = "Stop", Source = "user" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([textMessage]);
await termination.InvokeExpectingStopAsync([stopMessage]);
await termination.InvokeExpectingNullAsync([textMessage, otherMessage]);
await termination.InvokeExpectingStopAsync([textMessage, stopMessage], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task Test_TextMesssageTermination()
{
TextMessageTermination termination = new();
termination.IsTerminated.Should().BeFalse();
TextMessage userMessage = new() { Content = "Hello", Source = "user" };
TextMessage agentMessage = new() { Content = "World", Source = "agent" };
StopMessage stopMessage = new() { Content = "Stop", Source = "user" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingStopAsync([userMessage]);
await termination.InvokeExpectingStopAsync([agentMessage]);
await termination.InvokeExpectingNullAsync([stopMessage]);
termination = new("user");
await termination.InvokeExpectingNullAsync([agentMessage]);
await termination.InvokeExpectingNullAsync([stopMessage]);
await termination.InvokeExpectingStopAsync([userMessage], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task MaxMessageTermination()
{
MaxMessageTermination termination = new(2);
termination.IsTerminated.Should().BeFalse();
TextMessage textMessage = new() { Content = "Hello", Source = "user" };
TextMessage otherMessage = new() { Content = "World", Source = "agent" };
UserInputRequestedEvent uiRequest = new() { Source = "agent", RequestId = "1" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([textMessage]);
await termination.InvokeExpectingStopAsync([textMessage, otherMessage]);
await termination.InvokeExpectingNullAsync([textMessage, uiRequest]);
termination = new(2, includeAgentEvent: true);
await termination.InvokeExpectingStopAsync([textMessage, uiRequest], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task Test_TextMentionTermination()
{
TextMentionTermination termination = new("stop");
termination.IsTerminated.Should().BeFalse();
TextMessage textMessage = new() { Content = "Hello", Source = "user" };
TextMessage userStopMessage = new() { Content = "stop", Source = "user" };
TextMessage agentStopMessage = new() { Content = "stop", Source = "agent" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([textMessage]);
await termination.InvokeExpectingStopAsync([userStopMessage]);
termination = new("stop", sources: ["agent"]);
await termination.InvokeExpectingNullAsync([textMessage]);
await termination.InvokeExpectingNullAsync([userStopMessage]);
await termination.InvokeExpectingStopAsync([agentStopMessage], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task Text_TokenUsageTermination()
{
TokenUsageTermination termination = new(10);
termination.IsTerminated.Should().BeFalse();
RequestUsage usage_10_10 = new() { CompletionTokens = 10, PromptTokens = 10 };
RequestUsage usage_01_01 = new() { CompletionTokens = 1, PromptTokens = 1 };
RequestUsage usage_05_00 = new() { CompletionTokens = 5, PromptTokens = 0 };
RequestUsage usage_00_05 = new() { CompletionTokens = 0, PromptTokens = 5 };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingStopAsync([
new TextMessage { Content = "Hello", Source = "user", ModelUsage = usage_10_10 },
]);
await termination.InvokeExpectingNullAsync([
new TextMessage { Content = "Hello", Source = "user", ModelUsage = usage_01_01 },
new TextMessage { Content = "World", Source = "agent", ModelUsage = usage_01_01 },
]);
await termination.InvokeExpectingStopAsync([
new TextMessage { Content = "Hello", Source = "user", ModelUsage = usage_05_00 },
new TextMessage { Content = "World", Source = "agent", ModelUsage = usage_00_05 },
], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
public class AgentTextEvent : AgentEvent
{
public required string Content { get; set; }
public override Extensions.AI.ChatMessage ToCompletionClientMessage(ChatRole role)
{
return new Extensions.AI.ChatMessage(ChatRole.Assistant, this.Content);
}
}
[Fact]
public async Task Text_Termination_AndCombinator()
{
ITerminationCondition lhsClause = new MaxMessageTermination(2);
ITerminationCondition rhsClause = new TextMentionTermination("stop");
ITerminationCondition termination = lhsClause & rhsClause;
termination.IsTerminated.Should().BeFalse();
TextMessage userMessage = new() { Content = "Hello", Source = "user" };
AgentTextEvent agentMessage = new() { Content = "World", Source = "agent" };
TextMessage userStopMessage = new() { Content = "stop", Source = "user" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([userMessage]);
await termination.InvokeExpectingNullAsync([userMessage, agentMessage], reset: false);
lhsClause.IsTerminated.Should().BeFalse();
rhsClause.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingStopAsync([userStopMessage], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
await termination.InvokeExpectingFailureAsync([], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
termination.Reset();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([userMessage, agentMessage], reset: false);
lhsClause.IsTerminated.Should().BeFalse();
rhsClause.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([userMessage], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeFalse();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([userMessage], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeFalse();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingStopAsync([userStopMessage], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
await termination.InvokeExpectingFailureAsync([], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
termination.Reset();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([agentMessage, userStopMessage], reset: false);
lhsClause.IsTerminated.Should().BeFalse();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingStopAsync([userMessage], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
await termination.InvokeExpectingFailureAsync([], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task Test_Termination_OrCombiner()
{
ITerminationCondition lhsClause = new MaxMessageTermination(3);
ITerminationCondition rhsClause = new TextMentionTermination("stop");
ITerminationCondition termination = lhsClause | rhsClause;
termination.IsTerminated.Should().BeFalse();
TextMessage userMessage = new() { Content = "Hello", Source = "user" };
AgentTextEvent agentMessage = new() { Content = "World", Source = "agent" };
TextMessage userStopMessage = new() { Content = "stop", Source = "user" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([userMessage]);
await termination.InvokeExpectingNullAsync([userMessage, agentMessage]);
await termination.InvokeExpectingNullAsync([userMessage, agentMessage, userMessage], reset: false);
lhsClause.IsTerminated.Should().BeFalse();
rhsClause.IsTerminated.Should().BeFalse();
termination.IsTerminated.Should().BeFalse();
termination.Reset();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingStopAsync([userMessage, agentMessage, userStopMessage], reset: false);
lhsClause.IsTerminated.Should().BeFalse();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
await termination.InvokeExpectingFailureAsync([], reset: false);
lhsClause.IsTerminated.Should().BeFalse();
rhsClause.IsTerminated.Should().BeTrue();
termination.IsTerminated.Should().BeTrue();
termination.Reset();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingStopAsync([userMessage, userMessage, userMessage], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeFalse();
termination.IsTerminated.Should().BeTrue();
await termination.InvokeExpectingFailureAsync([], reset: false);
lhsClause.IsTerminated.Should().BeTrue();
rhsClause.IsTerminated.Should().BeFalse();
termination.IsTerminated.Should().BeTrue();
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
[Fact]
public async Task Test_TimeoutTermination()
{
TextMessage userMessage = new() { Content = "Hello", Source = "user" };
TimeoutTermination termination = new(0.15f);
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([]);
await Task.Delay(TimeSpan.FromSeconds(0.20f));
await termination.InvokeExpectingStopAsync([], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([userMessage]);
await Task.Delay(TimeSpan.FromSeconds(0.20f));
await termination.InvokeExpectingStopAsync([], reset: false);
}
[Fact]
public async Task Test_ExternalTermination()
{
ExternalTermination termination = new();
termination.IsTerminated.Should().BeFalse();
TextMessage userMessage = new() { Content = "Hello", Source = "user" };
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([userMessage]);
termination.Set();
termination.IsTerminated.Should().BeFalse(); // We only terminate on the next check
await termination.InvokeExpectingStopAsync([], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
await termination.InvokeExpectingNullAsync([userMessage]);
}
private ToolCallRequestEvent CreateFunctionRequest(string functionName, string id = "1", string arguments = "")
{
ToolCallRequestEvent result = new ToolCallRequestEvent
{
Source = "agent"
};
result.Content.Add(
new FunctionCall
{
Id = id,
Name = functionName,
Arguments = arguments,
});
return result;
}
private ToolCallExecutionEvent CreateFunctionResponse(string functionName, string id = "1", string content = "")
{
ToolCallExecutionEvent result = new ToolCallExecutionEvent
{
Source = "agent"
};
result.Content.Add(
new FunctionExecutionResult
{
Id = id,
Name = functionName,
Content = content,
});
return result;
}
[Fact]
public async Task Test_FunctionCallTermination()
{
FunctionCallTermination termination = new("test_function");
termination.IsTerminated.Should().BeFalse();
TextMessage userMessage = new() { Content = "Hello", Source = "user" };
ToolCallRequestEvent toolCallRequest = CreateFunctionRequest("test_function");
ToolCallExecutionEvent testExecution = CreateFunctionResponse("test_function");
ToolCallExecutionEvent otherExecution = CreateFunctionResponse("other_function");
await termination.InvokeExpectingNullAsync([]);
await termination.InvokeExpectingNullAsync([userMessage]);
await termination.InvokeExpectingNullAsync([toolCallRequest]);
await termination.InvokeExpectingNullAsync([otherExecution]);
await termination.InvokeExpectingStopAsync([testExecution], reset: false);
await termination.InvokeExpectingFailureAsync([], reset: false);
termination.Reset();
termination.IsTerminated.Should().BeFalse();
}
}

Some files were not shown because too many files have changed in this diff Show More