mirror of https://github.com/microsoft/autogen.git
Compare commits
No commits in common. "main" and "python-v0.4.6" have entirely different histories.
main
...
python-v0.
|
@ -1,5 +1,5 @@
|
|||
# Note: You can use any Debian/Ubuntu based image you want.
|
||||
FROM mcr.microsoft.com/devcontainers/base:ubuntu
|
||||
FROM mcr.microsoft.com/devcontainers/universal:2
|
||||
|
||||
# [Optional] Uncomment this section to install additional OS packages.
|
||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
|
|
|
@ -20,10 +20,7 @@
|
|||
},
|
||||
"ghcr.io/elanhasson/devcontainer-features/dotnet-aspire-daily:1": {},
|
||||
"ghcr.io/devcontainers/features/azure-cli:1": {},
|
||||
"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": {}
|
||||
"ghcr.io/azure/azure-dev/azd:0": {}
|
||||
},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
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.5"
|
||||
- "Python 0.5.4"
|
||||
- "Python 0.5.3"
|
||||
- "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
|
|
@ -1,47 +0,0 @@
|
|||
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
|
|
@ -1,20 +0,0 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
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
|
|
@ -1,8 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
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.
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
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
|
|
@ -12,6 +12,6 @@
|
|||
|
||||
## 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 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.
|
||||
|
|
|
@ -181,47 +181,9 @@ 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]
|
||||
needs: [test]
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
|
|
|
@ -106,11 +106,6 @@ jobs:
|
|||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
- if: matrix.build-mode == 'manual'
|
||||
name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
working-directory: dotnet
|
||||
|
|
|
@ -33,22 +33,27 @@ 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.5.5", dest-dir: stable, uv-version: "0.5.13", sphinx-release-override: "stable" },
|
||||
{ ref: "python-v0.4.5", dest-dir: stable, uv-version: "0.5.13", sphinx-release-override: "stable" },
|
||||
{ ref: "v0.4.0.dev0", dest-dir: "0.4.0.dev0", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev1", dest-dir: "0.4.0.dev1", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev2", dest-dir: "0.4.0.dev2", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev3", dest-dir: "0.4.0.dev3", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev4", dest-dir: "0.4.0.dev4", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev5", dest-dir: "0.4.0.dev5", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev6", dest-dir: "0.4.0.dev6", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev7", dest-dir: "0.4.0.dev7", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev8", dest-dir: "0.4.0.dev8", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev9", dest-dir: "0.4.0.dev9", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev10", dest-dir: "0.4.0.dev10", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev11", dest-dir: "0.4.0.dev11", uv-version: "0.5.11", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev12", dest-dir: "0.4.0.dev12", uv-version: "0.5.13", sphinx-release-override: "" },
|
||||
{ ref: "v0.4.0.dev13", dest-dir: "0.4.0.dev13", uv-version: "0.5.13", sphinx-release-override: "" },
|
||||
{ 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: "" },
|
||||
{ ref: "v0.4.3", dest-dir: "0.4.3", uv-version: "0.5.13", sphinx-release-override: "" },
|
||||
{ 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: "" },
|
||||
{ ref: "python-v0.5.3", dest-dir: "0.5.3", uv-version: "0.5.13", sphinx-release-override: "" },
|
||||
{ ref: "python-v0.5.4", dest-dir: "0.5.4", uv-version: "0.5.13", sphinx-release-override: "" },
|
||||
{ ref: "python-v0.5.5", dest-dir: "0.5.5", uv-version: "0.5.13", sphinx-release-override: "" },
|
||||
]
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
@ -81,10 +81,6 @@ jobs:
|
|||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore -bl
|
||||
- name: Format check
|
||||
|
@ -173,7 +169,7 @@ jobs:
|
|||
dotnet-version: '9.0.x'
|
||||
- name: Install Temp Global.JSON
|
||||
run: |
|
||||
echo "{\"sdk\": {\"version\": \"9.0\"}}" > global.json
|
||||
echo "{\"sdk\": {\"version\": \"9.0.101\"}}" > global.json
|
||||
- name: Install .NET Aspire workload
|
||||
run: dotnet workload install aspire
|
||||
- name: Install dev certs
|
||||
|
@ -211,10 +207,6 @@ jobs:
|
|||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
- name: publish AOT testApp, assert static analysis warning count, and run the app
|
||||
shell: pwsh
|
||||
|
@ -257,10 +249,6 @@ jobs:
|
|||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
global-json-file: dotnet/global.json
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
- name: Install dev certs
|
||||
run: dotnet --version && dotnet dev-certs https --trust
|
||||
- name: Restore dependencies
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
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
|
|
@ -6,9 +6,10 @@ on:
|
|||
- "0.2.*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to deploy the package'
|
||||
branch:
|
||||
description: 'Branch to deploy the package'
|
||||
required: true
|
||||
default: '0.2'
|
||||
permissions: {}
|
||||
jobs:
|
||||
deploy:
|
||||
|
@ -26,7 +27,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
- name: Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
|
|
|
@ -198,7 +198,4 @@ notebook/coding
|
|||
artifacts
|
||||
|
||||
# project data
|
||||
registry.json
|
||||
|
||||
# files created by the gitty agent in python/samples/gitty
|
||||
.gitty/
|
||||
registry.json
|
|
@ -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
|
||||
3. 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
|
||||
2. 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`
|
||||
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
|
||||
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
|
||||
|
||||
## Triage process
|
||||
|
||||
|
|
44
README.md
44
README.md
|
@ -47,24 +47,22 @@ from autogen_agentchat.agents import AssistantAgent
|
|||
from autogen_ext.models.openai import OpenAIChatCompletionClient
|
||||
|
||||
async def main() -> None:
|
||||
model_client = OpenAIChatCompletionClient(model="gpt-4o")
|
||||
agent = AssistantAgent("assistant", model_client=model_client)
|
||||
agent = AssistantAgent("assistant", OpenAIChatCompletionClient(model="gpt-4o"))
|
||||
print(await agent.run(task="Say 'Hello World!'"))
|
||||
await model_client.close()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Web Browsing Agent Team
|
||||
### Team
|
||||
|
||||
Create a group chat team with a web surfer agent and a user proxy agent
|
||||
Create a group chat team with an assistant agent, 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 UserProxyAgent
|
||||
from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
|
||||
from autogen_agentchat.conditions import TextMentionTermination
|
||||
from autogen_agentchat.teams import RoundRobinGroupChat
|
||||
from autogen_agentchat.ui import Console
|
||||
|
@ -73,21 +71,12 @@ from autogen_ext.agents.web_surfer import MultimodalWebSurfer
|
|||
|
||||
async def main() -> None:
|
||||
model_client = OpenAIChatCompletionClient(model="gpt-4o")
|
||||
# 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.
|
||||
assistant = AssistantAgent("assistant", model_client)
|
||||
web_surfer = MultimodalWebSurfer("web_surfer", model_client)
|
||||
user_proxy = UserProxyAgent("user_proxy")
|
||||
# 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()
|
||||
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."))
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
@ -112,7 +101,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 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.
|
||||
- [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.
|
||||
- [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_:
|
||||
|
@ -124,7 +113,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-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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -134,14 +123,15 @@ With AutoGen you get to join and contribute to a thriving ecosystem. We host wee
|
|||
|
||||
| | [](./python) | [](./dotnet) | [](./python/packages/autogen-studio) |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Installation | [](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/installation.html) | [](https://microsoft.github.io/autogen/dotnet/dev/core/installation.html) | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/installation.html) |
|
||||
| Quickstart | [](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/quickstart.html#) | [](https://microsoft.github.io/autogen/dotnet/dev/core/index.html) | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
|
||||
| Tutorial | [](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/index.html) | [](https://microsoft.github.io/autogen/dotnet/dev/core/tutorial.html) | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
|
||||
| API Reference | [](https://microsoft.github.io/autogen/stable/reference/index.html#) | [](https://microsoft.github.io/autogen/dotnet/dev/api/Microsoft.AutoGen.Contracts.html) | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html) |
|
||||
| Packages | [](https://pypi.org/project/autogen-core/) <br> [](https://pypi.org/project/autogen-agentchat/) <br> [](https://pypi.org/project/autogen-ext/) | [](https://www.nuget.org/packages/Microsoft.AutoGen.Contracts/) <br> [](https://www.nuget.org/packages/Microsoft.AutoGen.Core/) <br> [](https://www.nuget.org/packages/Microsoft.AutoGen.Core.Grpc/) <br> [](https://www.nuget.org/packages/Microsoft.AutoGen.RuntimeGateway.Grpc/) | [](https://pypi.org/project/autogenstudio/) |
|
||||
| Installation | [](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/installation.html) | \* | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/installation.html) |
|
||||
| Quickstart | [](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/quickstart.html#) | \* | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
|
||||
| Tutorial | [](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/index.html) | \* | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html#) |
|
||||
| API Reference | [](https://microsoft.github.io/autogen/stable/reference/index.html#) | \* | [](https://microsoft.github.io/autogen/stable/user-guide/autogenstudio-user-guide/usage.html) |
|
||||
| Packages | [](https://pypi.org/project/autogen-core/) <br> [](https://pypi.org/project/autogen-agentchat/) <br> [](https://pypi.org/project/autogen-ext/) | \* | [](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!
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 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}:` - 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}: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.
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
# How to build and run the website
|
||||
## How to build and run the website
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- dotnet 8.0 or later
|
||||
|
||||
## Build
|
||||
### Prerequisites
|
||||
- dotnet 7.0 or later
|
||||
|
||||
### Build
|
||||
Firstly, go to autogen/dotnet folder and run the following command to build the website:
|
||||
|
||||
```bash
|
||||
dotnet tool restore
|
||||
dotnet tool run docfx ../docs/dotnet/docfx.json --serve
|
||||
dotnet tool run docfx website/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.
|
|
@ -1,165 +1,7 @@
|
|||
# AutoGen Core
|
||||
|
||||
AutoGen Core for .NET follows the same concepts and conventions of its Python counterpart. In fact, in order to understand the concepts in the .NET version, we recommend reading the [Python documentation](https://microsoft.github.io/autogen/stable/) first. Unless otherwise stated, the concepts in the Python version map to .NET.
|
||||
AutoGen Core for .NET follows the same concepts and conventions of its Python counterpart. In fact, in order to understand the concepts in the .NET version, we recommend reading the Python documentation first. Unless otherwise stated, the concepts in the Python version map to .NET.
|
||||
|
||||
Any important differences between the language versions are documented in the [Differences from Python](./differences-from-python.md) section. For things that only affect a given language, such as dependency injection or host builder patterns, these will not be specified in the differences document.
|
||||
|
||||
## 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
|
||||
|
||||
To create an agent, you can inherit from BaseAgent and implement event handlers for the events you care about. Here is a minimal example demonstrating how to inherit from BaseAgent and implement an event handler:
|
||||
|
||||
```csharp
|
||||
public class MyAgent : BaseAgent, IHandle<MyMessage>
|
||||
{
|
||||
// ...
|
||||
public async ValueTask HandleAsync(MyMessage item, MessageContext context)
|
||||
{
|
||||
// ...logic here...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By overriding BaseAgent, you gain access to the runtime and logging utilities, and by implementing IHandle<T>, you can easily define event-handling methods for your custom messages.
|
||||
|
||||
### Running an Agent in an Application
|
||||
|
||||
To run your agent in an application, you can use the `AgentsAppBuilder` class. Here is an example of how to run an agent 'HelloAgent' in an application:
|
||||
|
||||
```csharp
|
||||
AgentsAppBuilder appBuilder = new AgentsAppBuilder()
|
||||
.UseInProcessRuntime(deliverToSelf: true)
|
||||
.AddAgent<HelloAgent>("HelloAgent");
|
||||
|
||||
var app = await appBuilder.BuildAsync();
|
||||
|
||||
// start the app by publishing a message to the runtime
|
||||
await app.PublishMessageAsync(new NewMessageReceived
|
||||
{
|
||||
Message = "Hello from .NET"
|
||||
}, new TopicId("HelloTopic"));
|
||||
|
||||
// Wait for shutdown
|
||||
await app.WaitForShutdownAsync();
|
||||
```
|
||||
|
||||
## .NET SDK Runtimes
|
||||
|
||||
The .NET SDK includes both an InMemory Single Process Runtime and a Remote, Distributed Runtime meant for running your agents in the cloud. The Distributed Runtime supports running agents in python and in .NET, allowing those agents to talk to one another. The distributed runtime uses Microsoft Orleans to provide resilience, persistence, and integration with messaging services such as Azure Event Hubs. The xlang functionality requires that your agent's Messages are serializable as CloudEvents. The messages are exchanged as CloudEvents over Grpc, and the runtime takes care of ensuring that the messages are delivered to the correct agents.
|
||||
|
||||
To use the Distributed Runtime, you will need to add the following package to your project:
|
||||
|
||||
```bash
|
||||
dotnet add package Microsoft.AutoGen.Core.Grpc
|
||||
```
|
||||
|
||||
This is the package that runs in the application with your agent(s) and connects to the distributed system.
|
||||
|
||||
To Run the backend/server side you need:
|
||||
|
||||
```bash
|
||||
dotnet add package Microsoft.AutoGen.RuntimeGateway
|
||||
dotnet add package Microsoft.AutoGen.AgentHost
|
||||
```
|
||||
|
||||
You can run the backend on its own:
|
||||
|
||||
```bash
|
||||
dotnet run --project Microsoft.AutoGen.AgentHost
|
||||
```
|
||||
|
||||
or you can run iclude it inside your own application:
|
||||
|
||||
```csharp
|
||||
using Microsoft.AutoGen.RuntimeGateway;
|
||||
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).
|
||||
|
||||
```csharp
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Program.cs
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
var backend = builder.AddProject<Projects.Microsoft_AutoGen_AgentHost>("backend").WithExternalHttpEndpoints();
|
||||
var client = builder.AddProject<Projects.HelloAgent>("HelloAgentsDotNET")
|
||||
.WithReference(backend)
|
||||
.WithEnvironment("AGENT_HOST", backend.GetEndpoint("https"))
|
||||
.WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true")
|
||||
.WaitFor(backend);
|
||||
// xlang is over http for now - in prod use TLS between containers
|
||||
builder.AddPythonApp("HelloAgentsPython", "../../../../python/samples/core_xlang_hello_python_agent", "hello_python_agent.py", "../../.venv")
|
||||
.WithReference(backend)
|
||||
.WithEnvironment("AGENT_HOST", backend.GetEndpoint("http"))
|
||||
.WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true")
|
||||
.WithEnvironment("GRPC_DNS_RESOLVER", "native")
|
||||
.WithOtlpExporter()
|
||||
.WaitFor(client);
|
||||
using var app = builder.Build();
|
||||
await app.StartAsync();
|
||||
var url = backend.GetEndpoint("http").Url;
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
"Microsoft.AspNetCore": "Information",
|
||||
"Microsoft": "Information",
|
||||
"Microsoft.Orleans": "Warning",
|
||||
"Orleans.Runtime": "Error",
|
||||
"Grpc": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http2"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Defining Message Types in Protocol Buffers
|
||||
|
||||
A convenient way to define common event or message types to be used in both python and .NET agents is to define your events. This is covered here: [Using Protocol Buffers to Define Message Types](./protobuf-message-types.md).
|
||||
For .NET we are starting with the core functionality and will be expanding support progressively. So far the core abstractions of Agent and Runtime are available. The InProcessRuntime is the only runtime available at this time. We will be expanding to cross language support in upcoming releases.
|
||||
|
|
|
@ -20,29 +20,3 @@ 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
|
||||
```
|
|
@ -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
|
|
@ -5,11 +5,7 @@
|
|||
{
|
||||
"files": [
|
||||
"src/Microsoft.AutoGen/Core/**/*.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/Microsoft.AutoGen/Contracts/**/*.csproj"
|
||||
],
|
||||
"src": "../../dotnet/"
|
||||
}
|
||||
|
@ -73,4 +69,4 @@
|
|||
"keepFileLink": false,
|
||||
"disableGitFeatures": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,15 @@
|
|||
---
|
||||
_disableAffix: true
|
||||
---
|
||||
<style>
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.subheader {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="center">
|
||||
<h1>AutoGen .NET</h1>
|
||||
|
@ -14,46 +23,7 @@ _disableAffix: true
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Core</h5>
|
||||
<p>
|
||||
|
||||
[](https://github.com/microsoft/autogen/actions/workflows/dotnet-build.yml)
|
||||
[](https://badge.fury.io/nu/Microsoft.AutoGen.Contracts)
|
||||
[](https://badge.fury.io/nu/Microsoft.AutoGen.Core)
|
||||
[](https://badge.fury.io/nu/Microsoft.AutoGen.Core.Grpc)
|
||||
[](https://badge.fury.io/nu/Microsoft.AutoGen.RuntimeGateway.Grpc)
|
||||
[](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>
|
||||
|
|
|
@ -2,114 +2,3 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -1,88 +1,18 @@
|
|||
[
|
||||
{
|
||||
"name": "0.4.5 (stable)",
|
||||
"version": "stable",
|
||||
"url": "/autogen/stable/",
|
||||
"preferred": true
|
||||
},
|
||||
{
|
||||
"name": "dev (main)",
|
||||
"version": "dev",
|
||||
"url": "/autogen/dev/"
|
||||
},
|
||||
{
|
||||
"name": "0.5.5 (stable)",
|
||||
"version": "stable",
|
||||
"url": "/autogen/stable/",
|
||||
"preferred": true
|
||||
},
|
||||
{
|
||||
"name": "0.5.4",
|
||||
"version": "0.5.4",
|
||||
"url": "/autogen/0.5.4/"
|
||||
},
|
||||
{
|
||||
"name": "0.5.3",
|
||||
"version": "0.5.3",
|
||||
"url": "/autogen/0.5.3/"
|
||||
},
|
||||
{
|
||||
"name": "0.5.2",
|
||||
"version": "0.5.2",
|
||||
"url": "/autogen/0.5.2/"
|
||||
},
|
||||
{
|
||||
"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/"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
"version": "0.1.205",
|
||||
"commands": [
|
||||
"dotnet-repl"
|
||||
],
|
||||
"rollForward": true
|
||||
]
|
||||
},
|
||||
"docfx": {
|
||||
"version": "2.67.5",
|
||||
"commands": [
|
||||
"docfx"
|
||||
],
|
||||
"rollForward": true
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ foreach ($line in $($publishOutput -split "`r`n"))
|
|||
}
|
||||
}
|
||||
|
||||
pushd $rootDirectory/artifacts/bin/AutoGen.AotCompatibility.Tests/release/native
|
||||
pushd $rootDirectory/artifacts/bin/AutoGen.AotCompatibility.Tests/release
|
||||
|
||||
Write-Host "Executing test App..."
|
||||
./AutoGen.AotCompatibility.Tests
|
||||
|
|
|
@ -140,13 +140,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AutoGen.AgentChat
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AutoGen.AgentChat.Tests", "test\Microsoft.AutoGen.AgentChat.Tests\Microsoft.AutoGen.AgentChat.Tests.csproj", "{217A4F86-8ADD-4998-90BA-880092A019F5}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.AutoGen.Integration.Tests.AppHosts", "Microsoft.AutoGen.Integration.Tests.AppHosts", "{D1C2B0BB-1276-4146-A699-D1983AE8ED04}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloAgentTests", "test\Microsoft.AutoGen.Integration.Tests.AppHosts\HelloAgentTests\HelloAgentTests.csproj", "{CD10E29A-725E-4BEF-9CFF-6C0E0A652926}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InMemoryTests.AppHost", "test\Microsoft.AutoGen.Integration.Tests.AppHosts\InMemoryTests.AppHost\InMemoryTests.AppHost.csproj", "{1E4E1ED4-7701-4A05-A861-64461C3B1EE3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XlangTests.AppHost", "test\Microsoft.AutoGen.Integration.Tests.AppHosts\XLangTests.AppHost\XlangTests.AppHost.csproj", "{62CDFB27-3B02-4D4B-B789-8AAD5E20688A}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloAgent.AppHost", "test\Microsoft.AutoGen.Integration.Tests.AppHosts\HelloAgent.AppHost\HelloAgent.AppHost.csproj", "{0C371D65-7EF9-44EA-8128-A105DA82A80E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -378,18 +372,6 @@ Global
|
|||
{0C371D65-7EF9-44EA-8128-A105DA82A80E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0C371D65-7EF9-44EA-8128-A105DA82A80E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0C371D65-7EF9-44EA-8128-A105DA82A80E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CD10E29A-725E-4BEF-9CFF-6C0E0A652926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CD10E29A-725E-4BEF-9CFF-6C0E0A652926}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CD10E29A-725E-4BEF-9CFF-6C0E0A652926}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CD10E29A-725E-4BEF-9CFF-6C0E0A652926}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1E4E1ED4-7701-4A05-A861-64461C3B1EE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1E4E1ED4-7701-4A05-A861-64461C3B1EE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E4E1ED4-7701-4A05-A861-64461C3B1EE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E4E1ED4-7701-4A05-A861-64461C3B1EE3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{62CDFB27-3B02-4D4B-B789-8AAD5E20688A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62CDFB27-3B02-4D4B-B789-8AAD5E20688A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62CDFB27-3B02-4D4B-B789-8AAD5E20688A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62CDFB27-3B02-4D4B-B789-8AAD5E20688A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -454,10 +436,7 @@ Global
|
|||
{EF954ED3-87D5-40F1-8557-E7179F43EA0E} = {18BF8DD7-0585-48BF-8F97-AD333080CE06}
|
||||
{7F828599-56E8-4597-8F68-EE26FD631417} = {18BF8DD7-0585-48BF-8F97-AD333080CE06}
|
||||
{217A4F86-8ADD-4998-90BA-880092A019F5} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64}
|
||||
{D1C2B0BB-1276-4146-A699-D1983AE8ED04} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64}
|
||||
{CD10E29A-725E-4BEF-9CFF-6C0E0A652926} = {D1C2B0BB-1276-4146-A699-D1983AE8ED04}
|
||||
{1E4E1ED4-7701-4A05-A861-64461C3B1EE3} = {D1C2B0BB-1276-4146-A699-D1983AE8ED04}
|
||||
{62CDFB27-3B02-4D4B-B789-8AAD5E20688A} = {D1C2B0BB-1276-4146-A699-D1983AE8ED04}
|
||||
{0C371D65-7EF9-44EA-8128-A105DA82A80E} = {18BF8DD7-0585-48BF-8F97-AD333080CE06}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B}
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<MicrosoftSemanticKernelVersion>1.22.0</MicrosoftSemanticKernelVersion>
|
||||
<MicrosoftSemanticKernelStableVersion>1.45.0</MicrosoftSemanticKernelStableVersion>
|
||||
<MicrosoftSemanticKernelPreviewVersion>$(MicrosoftSemanticKernelStableVersion)-preview</MicrosoftSemanticKernelPreviewVersion>
|
||||
<MicrosoftSemanticKernelAlphaVersion>$(MicrosoftSemanticKernelStableVersion)-alpha</MicrosoftSemanticKernelAlphaVersion>
|
||||
<MicrosoftExtensionsAIVersion>9.3.0-preview.1.25161.3</MicrosoftExtensionsAIVersion>
|
||||
<MicrosoftSemanticKernelExperimentalVersion>1.22.0-alpha</MicrosoftSemanticKernelExperimentalVersion>
|
||||
<MicrosoftExtensionsAIVersion>9.0.0-preview.9.24525.1</MicrosoftExtensionsAIVersion>
|
||||
<MicrosoftExtensionConfiguration>9.0.0</MicrosoftExtensionConfiguration>
|
||||
<MicrosoftExtensionDependencyInjection>9.0.3</MicrosoftExtensionDependencyInjection>
|
||||
<MicrosoftExtensionDependencyInjection>9.0.0</MicrosoftExtensionDependencyInjection>
|
||||
<MicrosoftExtensionLogging>9.0.0</MicrosoftExtensionLogging>
|
||||
<MicrosoftExtensionOptions>9.0.0</MicrosoftExtensionOptions>
|
||||
<MicrosoftOrleans>9.0.1</MicrosoftOrleans>
|
||||
|
@ -20,7 +18,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="9.0.0-preview.5.24551.3" />
|
||||
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="8.0.1-preview.8.24267.1" />
|
||||
<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" />
|
||||
|
@ -29,10 +27,9 @@
|
|||
<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.2.0-beta.4" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.1.0-beta.2" />
|
||||
<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" />
|
||||
|
@ -103,18 +100,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="$(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.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.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.2.0-beta.4" />
|
||||
<PackageVersion Include="OpenAI" Version="2.1.0-beta.2" />
|
||||
<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)" />
|
||||
|
@ -130,7 +127,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.3" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
|
||||
<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" />
|
||||
|
|
|
@ -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.3</VersionPrefixForAutoGen0_2>
|
||||
<VersionPrefixForAutoGen0_2>0.2.2</VersionPrefixForAutoGen0_2>
|
||||
<Authors>Microsoft</Authors>
|
||||
<PackageProjectUrl>https://microsoft.github.io/autogen-for-net/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/microsoft/autogen</RepositoryUrl>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "9.0.100",
|
||||
"rollForward": "latestFeature"
|
||||
"version": "8.0.401",
|
||||
"rollForward": "latestMinor"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: settings);
|
||||
.ToSemanticKernelAgent(name: "assistant", systemMessage: "You control the light", settings);
|
||||
|
||||
// Send a message to the skAgent, the skAgent supports the following message types:
|
||||
// - IMessage<ChatMessageContent>
|
||||
|
|
|
@ -42,6 +42,8 @@ public class Tool_Call_With_Ollama_And_LiteLLM
|
|||
});
|
||||
#endregion Create_tools
|
||||
#region Create_Agent
|
||||
var liteLLMUrl = "http://localhost:4000";
|
||||
|
||||
// api-key is not required for local server
|
||||
// so you can use any string here
|
||||
var openAIClient = new OpenAIClient(new ApiKeyCredential("api-key"), new OpenAIClientOptions
|
||||
|
@ -57,7 +59,7 @@ public class Tool_Call_With_Ollama_And_LiteLLM
|
|||
.RegisterMiddleware(functionMiddleware)
|
||||
.RegisterPrintMessage();
|
||||
|
||||
await agent.SendAsync("what's the weather in new york");
|
||||
var reply = await agent.SendAsync("what's the weather in new york");
|
||||
#endregion Create_Agent
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,6 @@ public class DummyAgent : IStreamingAgent
|
|||
foreach (var c in reply)
|
||||
{
|
||||
yield return new TextMessageUpdate(Role.Assistant, c.ToString(), this.Name);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// HelloAgent.cs
|
||||
|
||||
using Microsoft.AutoGen.Agents;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
using Microsoft.AutoGen.Core;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
|
|
@ -21,12 +21,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Contracts\Microsoft.AutoGen.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core.Grpc\Microsoft.AutoGen.Core.Grpc.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core\Microsoft.AutoGen.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core.Grpc\Microsoft.AutoGen.Core.Grpc.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="..\..\..\src\Microsoft.AutoGen\Agents\protos\agent_events.proto" Link="protos\agent_events.proto" />
|
||||
<Protobuf Include="..\protos\agent_events.proto" Link="protos\agent_events.proto" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,82 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Program.cs
|
||||
|
||||
using Microsoft.AutoGen.Agents;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
using Microsoft.AutoGen.Core;
|
||||
using Microsoft.AutoGen.Core.Grpc;
|
||||
using Samples;
|
||||
|
||||
string? hostAddress = null;
|
||||
bool in_host_address = false;
|
||||
bool sendHello = true;
|
||||
foreach (string arg in args)
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
case "--host":
|
||||
in_host_address = true;
|
||||
break;
|
||||
case "--nosend":
|
||||
sendHello = false;
|
||||
break;
|
||||
case "-h":
|
||||
case "--help":
|
||||
PrintHelp();
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
default:
|
||||
if (in_host_address)
|
||||
{
|
||||
hostAddress = arg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hostAddress ??= Environment.GetEnvironmentVariable("AGENT_HOST");
|
||||
var appBuilder = new AgentsAppBuilder(); // Create app builder
|
||||
// if we are using distributed, we need the AGENT_HOST var defined and then we will use the grpc runtime
|
||||
|
||||
bool usingGrpc = false;
|
||||
if (hostAddress is string agentHost)
|
||||
{
|
||||
usingGrpc = true;
|
||||
Console.WriteLine($"connecting to {agentHost}");
|
||||
appBuilder.AddGrpcAgentWorker(agentHost)
|
||||
.AddAgent<HelloAgent>("HelloAgent");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set up app builder for in-process runtime, allow message delivery to self, and add the Hello agent
|
||||
appBuilder.UseInProcessRuntime(deliverToSelf: true).AddAgent<HelloAgent>("HelloAgent");
|
||||
}
|
||||
// Set up app builder for in-process runtime, allow message delivery to self, and add the Hello agent
|
||||
AgentsAppBuilder appBuilder = new AgentsAppBuilder()
|
||||
.UseInProcessRuntime(deliverToSelf: true)
|
||||
.AddAgent<HelloAgent>("HelloAgent");
|
||||
var app = await appBuilder.BuildAsync(); // Build the app
|
||||
await app.StartAsync();
|
||||
// Create a custom message type from proto and define message
|
||||
|
||||
if (sendHello)
|
||||
{
|
||||
var message = new NewMessageReceived { Message = "Hello World!" };
|
||||
await app.PublishMessageAsync(message, new TopicId("HelloTopic")).ConfigureAwait(false); // Publish custom message (handler has been set in HelloAgent)
|
||||
}
|
||||
else if (!usingGrpc)
|
||||
{
|
||||
Console.Write("Warning: Using --nosend with the InProcessRuntime will hang. Terminating.");
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
|
||||
await app.WaitForShutdownAsync().ConfigureAwait(false); // Wait for shutdown from agent
|
||||
|
||||
static void PrintHelp()
|
||||
{
|
||||
/*
|
||||
HelloAgent [--host <hostAddress>] [--nosend]
|
||||
--host Use gRPC gateway at <hostAddress>; this can also be set using the AGENT_HOST Environment Variable
|
||||
--nosend Do not send the starting message. Note: This means HelloAgent will wait until some other agent will send
|
||||
that message. This will not work when using the InProcessRuntime.
|
||||
*/
|
||||
Console.WriteLine("HelloAgent [--host <hostAddress>] [--nosend]");
|
||||
Console.WriteLine(" --host \tUse gRPC gateway at <hostAddress>; this can also be set using the AGENT_HOST Environment Variable");
|
||||
Console.WriteLine(" --nosend \tDo not send the starting message. Note: This means HelloAgent will wait until some other agent will send");
|
||||
}
|
||||
NewMessageReceived message = new NewMessageReceived { Message = "Hello World!" };
|
||||
await app.PublishMessageAsync(message, new TopicId("HelloTopic")); // Publish custom message (handler has been set in HelloAgent)
|
||||
await app.WaitForShutdownAsync(); // Wait for shutdown from agent
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
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;
|
||||
|
||||
|
@ -71,48 +69,36 @@ public class FunctionContract
|
|||
/// </summary>
|
||||
public string? ReturnDescription { get; set; }
|
||||
|
||||
public static implicit operator FunctionContract(AIFunction function)
|
||||
public static implicit operator FunctionContract(AIFunctionMetadata metadata)
|
||||
{
|
||||
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 = 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,
|
||||
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)!],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -146,4 +132,29 @@ 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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).ToArray();
|
||||
this.functions = functions.Select(f => (FunctionContract)f.Metadata).ToArray();
|
||||
|
||||
this.functionMap = functions.Select(f => (f.Name, this.AIToolInvokeWrapper(f.InvokeAsync))).ToDictionary(f => f.Name, f => f.Item2);
|
||||
this.functionMap = functions.Select(f => (f.Metadata.Name, this.AIToolInvokeWrapper(f.InvokeAsync))).ToDictionary(f => f.Name, f => f.Item2);
|
||||
}
|
||||
|
||||
public string? Name { get; }
|
||||
|
|
|
@ -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", string? modelServiceId = null, PromptExecutionSettings? settings = null)
|
||||
public static SemanticKernelAgent ToSemanticKernelAgent(this Kernel kernel, string name, string systemMessage = "You are a helpful AI assistant", PromptExecutionSettings? settings = null)
|
||||
{
|
||||
return new SemanticKernelAgent(kernel, name, systemMessage, modelServiceId, settings);
|
||||
return new SemanticKernelAgent(kernel, name, systemMessage, settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -32,28 +32,17 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -63,7 +52,7 @@ public class SemanticKernelAgent : IStreamingAgent
|
|||
{
|
||||
var chatHistory = BuildChatHistory(messages);
|
||||
var option = BuildOption(options);
|
||||
var chatService = GetChatCompletionService();
|
||||
var chatService = _kernel.GetRequiredService<IChatCompletionService>();
|
||||
|
||||
var reply = await chatService.GetChatMessageContentsAsync(chatHistory, option, _kernel, cancellationToken);
|
||||
|
||||
|
@ -82,7 +71,7 @@ public class SemanticKernelAgent : IStreamingAgent
|
|||
{
|
||||
var chatHistory = BuildChatHistory(messages);
|
||||
var option = BuildOption(options);
|
||||
var chatService = GetChatCompletionService();
|
||||
var chatService = _kernel.GetRequiredService<IChatCompletionService>();
|
||||
var response = chatService.GetStreamingChatMessageContentsAsync(chatHistory, option, _kernel, cancellationToken);
|
||||
|
||||
await foreach (var content in response)
|
||||
|
@ -119,13 +108,6 @@ 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
|
||||
|
|
|
@ -26,9 +26,8 @@ public class SemanticKernelChatCompletionAgent : IAgent
|
|||
public async Task<IMessage> GenerateReplyAsync(IEnumerable<IMessage> messages, GenerateReplyOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var agentThread = new ChatHistoryAgentThread(BuildChatHistory(messages));
|
||||
var reply = await _chatCompletionAgent
|
||||
.InvokeAsync(agentThread, cancellationToken: cancellationToken)
|
||||
ChatMessageContent[] reply = await _chatCompletionAgent
|
||||
.InvokeAsync(BuildChatHistory(messages), cancellationToken: cancellationToken)
|
||||
.ToArrayAsync(cancellationToken: cancellationToken);
|
||||
|
||||
return reply.Length > 1
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// ChatAgent.cs
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
|
||||
namespace Microsoft.AutoGen.AgentChat.Abstractions;
|
||||
|
||||
|
@ -166,29 +165,29 @@ public class ChatStreamFrame : StreamingFrame<Response, AgentMessage>;
|
|||
/// </summary>
|
||||
public interface IChatAgent :
|
||||
IHandleChat<IEnumerable<ChatMessage>, Response>,
|
||||
IHandleStream<IEnumerable<ChatMessage>, ChatStreamFrame>,
|
||||
ISaveState
|
||||
IHandleStream<IEnumerable<ChatMessage>, ChatStreamFrame>
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the agent. This is used by team to uniquely identify the agent.It should be unique within the team.
|
||||
/// </summary>
|
||||
public AgentName Name { get; }
|
||||
AgentName Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of the agent. This is used by team to make decisions about which agents to use.The description
|
||||
/// should describe the agent's capabilities and how to interact with it.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The types of messages that the agent produces.
|
||||
/// </summary>
|
||||
public IEnumerable<Type> ProducedMessageTypes { get; } // TODO: Is there a way to make this part of the type somehow? Annotations, or IProduce<>? Do we ever actually access this?
|
||||
IEnumerable<Type> ProducedMessageTypes { get; } // TODO: Is there a way to make this part of the type somehow?
|
||||
// Annotations, or IProduce<>? Do we ever actually access this?
|
||||
|
||||
/// <summary>
|
||||
/// Reset the agent to its initialization state.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public ValueTask ResetAsync(CancellationToken cancellationToken);
|
||||
ValueTask ResetAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
// 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, ISaveState
|
||||
public interface ITeam : ITaskRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Reset the team and all its participants to its initial state.
|
||||
|
|
|
@ -5,22 +5,22 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions;
|
|||
|
||||
public interface IHandleChat<in TIn>
|
||||
{
|
||||
public ValueTask HandleAsync(TIn item)
|
||||
ValueTask HandleAsync(TIn item)
|
||||
{
|
||||
return this.HandleAsync(item, CancellationToken.None);
|
||||
}
|
||||
|
||||
public ValueTask HandleAsync(TIn item, CancellationToken cancellationToken);
|
||||
ValueTask HandleAsync(TIn item, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IHandleChat<in TIn, TOut> // TODO: Map this to IHandle<> somehow?
|
||||
{
|
||||
public ValueTask<TOut> HandleAsync(TIn item)
|
||||
ValueTask<TOut> HandleAsync(TIn item)
|
||||
{
|
||||
return this.HandleAsync(item, CancellationToken.None);
|
||||
}
|
||||
|
||||
public ValueTask<TOut> HandleAsync(TIn item, CancellationToken cancellationToken);
|
||||
ValueTask<TOut> HandleAsync(TIn item, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IHandleDefault : IHandleChat<object>
|
||||
|
@ -29,10 +29,10 @@ public interface IHandleDefault : IHandleChat<object>
|
|||
|
||||
public interface IHandleStream<in TIn, TOut>
|
||||
{
|
||||
public IAsyncEnumerable<TOut> StreamAsync(TIn item)
|
||||
IAsyncEnumerable<TOut> StreamAsync(TIn item)
|
||||
{
|
||||
return this.StreamAsync(item, CancellationToken.None);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<TOut> StreamAsync(TIn item, CancellationToken cancellationToken);
|
||||
IAsyncEnumerable<TOut> StreamAsync(TIn item, CancellationToken cancellationToken);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,7 @@
|
|||
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;
|
||||
|
@ -25,11 +22,6 @@ 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
|
||||
|
@ -124,7 +116,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="DataContent"/>.
|
||||
/// Thrown if the <paramref name="item"/> is not a <see cref="TextContent"/> or <see cref="ImageContent"/>.
|
||||
/// </exception>
|
||||
public static MultiModalData CheckTypeAndCreate(AIContent item)
|
||||
{
|
||||
|
@ -132,7 +124,7 @@ public struct MultiModalData
|
|||
{
|
||||
return new MultiModalData(text);
|
||||
}
|
||||
else if (item is DataContent image)
|
||||
else if (item is ImageContent image)
|
||||
{
|
||||
return new MultiModalData(image);
|
||||
}
|
||||
|
@ -163,10 +155,10 @@ public struct MultiModalData
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MultiModalData"/> with an <see cref="DataContent"/>.
|
||||
/// Initializes a new instance of the <see cref="MultiModalData"/> with an <see cref="ImageContent"/>.
|
||||
/// </summary>
|
||||
/// <param name="image">The image to wrap.</param>
|
||||
public MultiModalData(DataContent image)
|
||||
public MultiModalData(ImageContent image)
|
||||
{
|
||||
ContentType = Type.Image;
|
||||
AIContent = image;
|
||||
|
@ -254,12 +246,12 @@ public class MultiModalMessage : ChatMessage, IList<AIContent>
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a range of <see cref="DataContent"/> to the message.
|
||||
/// Adds a range of <see cref="ImageContent"/> to the message.
|
||||
/// </summary>
|
||||
/// <param name="images">The items to add.</param>
|
||||
public void AddRange(IEnumerable<DataContent> images)
|
||||
public void AddRange(IEnumerable<ImageContent> images)
|
||||
{
|
||||
foreach (DataContent image in images)
|
||||
foreach (ImageContent image in images)
|
||||
{
|
||||
this.Add(image);
|
||||
}
|
||||
|
@ -287,7 +279,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(DataContent image)
|
||||
public void Add(ImageContent image)
|
||||
{
|
||||
this.Content.Add(new(image));
|
||||
}
|
||||
|
@ -374,7 +366,7 @@ public class MultiModalMessage : ChatMessage, IList<AIContent>
|
|||
}
|
||||
|
||||
/// <inheritdoc cref="IList{ImageContent}.Insert(int, ImageContent)"/>
|
||||
public void Insert(int index, DataContent image)
|
||||
public void Insert(int index, ImageContent image)
|
||||
{
|
||||
this.Content.Insert(index, new(image));
|
||||
}
|
||||
|
@ -500,11 +492,6 @@ 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>
|
||||
|
@ -610,7 +597,7 @@ public static class CompletionChatMessageExtensions
|
|||
{
|
||||
contentBuilder.AppendLine(textContent.Text);
|
||||
}
|
||||
else if (content is DataContent)
|
||||
else if (content is ImageContent)
|
||||
{
|
||||
contentBuilder.AppendLine("[Image]");
|
||||
}
|
||||
|
@ -627,129 +614,3 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
// 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
|
|
@ -59,7 +59,7 @@ public interface ITaskRunner
|
|||
/// <param name="task">The task definition in text form.</param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>The result of running the task.</returns>
|
||||
public async ValueTask<TaskResult> RunAsync(string task, CancellationToken cancellationToken = default) =>
|
||||
async ValueTask<TaskResult> RunAsync(string task, CancellationToken cancellationToken = default) =>
|
||||
await this.RunAsync(ToMessage(task)!, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
|
@ -73,7 +73,7 @@ public interface ITaskRunner
|
|||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>The result of running the task.</returns>
|
||||
/// <exception cref="InvalidOperationException">If no response is generated.</exception>
|
||||
public async ValueTask<TaskResult> RunAsync(ChatMessage task, CancellationToken cancellationToken = default)
|
||||
async ValueTask<TaskResult> RunAsync(ChatMessage task, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await foreach (TaskFrame frame in this.StreamAsync(task, cancellationToken))
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ public interface ITaskRunner
|
|||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>A stream of <see cref="TaskFrame"/> containing internal messages and intermediate results followed by
|
||||
/// the final <see cref="TaskResult"/></returns>
|
||||
public IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken = default) =>
|
||||
IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken = default) =>
|
||||
this.StreamAsync(ToMessage(task), cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
|
@ -113,5 +113,5 @@ public interface ITaskRunner
|
|||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>A stream of <see cref="TaskFrame"/> containing internal messages and intermediate results followed by
|
||||
/// the final <see cref="TaskResult"/></returns>
|
||||
public IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, CancellationToken cancellationToken = default);
|
||||
IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
|
|
@ -3,29 +3,6 @@
|
|||
|
||||
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.
|
||||
///
|
||||
|
@ -35,15 +12,14 @@ public static class TerminationConditionExtensions
|
|||
///
|
||||
/// 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="TerminationConditionExtensions.Or"/> and
|
||||
/// <see cref="TerminationConditionExtensions.And"/> methods.
|
||||
/// Termination conditions can be combined using the <see cref="Or"/> and <see cref="And"/> methods.
|
||||
/// </summary>
|
||||
public interface ITerminationCondition
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the termination condition has been reached
|
||||
/// </summary>
|
||||
public bool IsTerminated { get; }
|
||||
bool IsTerminated { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Check if the conversation should be terminated based on the messages received
|
||||
|
@ -54,45 +30,31 @@ public interface ITerminationCondition
|
|||
/// <returns>A <see cref="StopMessage"/> if the conversation should be terminated, or <c>null</c>
|
||||
/// otherwise.</returns>
|
||||
/// <exception cref="TerminatedException">If the termination condition has already been reached.</exception>
|
||||
public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages);
|
||||
ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the termination condition.
|
||||
/// </summary>
|
||||
public void Reset();
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Combine two termination conditions with another using an associative, short-circuiting OR.
|
||||
/// Combine this termination condition with another using a logical OR.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
/// <param name="other">Another termination condition.</param>
|
||||
/// <returns>The combined termination condition, with appropriate short-circuiting.</returns>
|
||||
ITerminationCondition Or(ITerminationCondition other)
|
||||
{
|
||||
return left.Or(right);
|
||||
return new CombinerCondition(CombinerCondition.Or, this, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combine two termination conditions with another using an associative, short-circuiting AND.
|
||||
/// Combine this termination condition with another using a logical AND.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
/// <param name="other">Another termination condition.</param>
|
||||
/// <returns>The combined termination condition, with appropriate short-circuiting.</returns>
|
||||
ITerminationCondition And(ITerminationCondition other)
|
||||
{
|
||||
return left.And(right);
|
||||
return new CombinerCondition(CombinerCondition.And, this, other);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,4 +167,38 @@ 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Tools.cs
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
|
@ -9,6 +10,37 @@ 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;
|
||||
|
@ -22,6 +54,15 @@ 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?
|
||||
|
@ -41,10 +82,11 @@ public class ParameterSchema<T>(string name, bool isRequired = false, T? default
|
|||
/// </summary>
|
||||
public interface ITool
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
string Name { get; }
|
||||
string Description { get; }
|
||||
|
||||
public IEnumerable<ParameterSchema> Parameters { get; }
|
||||
public Type ReturnType { get; }
|
||||
|
||||
// TODO: State serialization
|
||||
|
||||
|
@ -94,15 +136,18 @@ public class AIFunctionTool(AIFunction aiFunction) : ITool
|
|||
public AIFunction AIFunction { get; } = aiFunction;
|
||||
|
||||
/// <inheritdoc cref="ITool.Name" />
|
||||
public string Name => this.AIFunction.Name;
|
||||
public string Name => this.AIFunction.Metadata.Name;
|
||||
|
||||
/// <inheritdoc cref="ITool.Description" />
|
||||
public string Description => this.AIFunction.Description;
|
||||
public string Description => this.AIFunction.Metadata.Description;
|
||||
|
||||
/// <inheritdoc cref="ITool.Parameters" />
|
||||
public IEnumerable<ParameterSchema> Parameters => from rawParameter in this.AIFunction.UnderlyingMethod!.GetParameters()
|
||||
public IEnumerable<ParameterSchema> Parameters => from rawParameter in this.AIFunction.Metadata.Parameters
|
||||
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);
|
||||
|
@ -119,6 +164,23 @@ public class CallableTool(string name, string description, Delegate callable)
|
|||
{
|
||||
internal static AIFunction CreateAIFunction(string name, string description, Delegate callable)
|
||||
{
|
||||
return AIFunctionFactory.Create(callable, name: name, description: description);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
// 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; }
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
// 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;
|
||||
|
@ -26,8 +24,7 @@ internal sealed class ChatAgentRouter : HostableAgentAdapter,
|
|||
IHandle<GroupChatStart>,
|
||||
IHandle<GroupChatAgentResponse>,
|
||||
IHandle<GroupChatRequestPublish>,
|
||||
IHandle<GroupChatReset>,
|
||||
ISaveState
|
||||
IHandle<GroupChatReset>
|
||||
{
|
||||
private readonly TopicId parentTopic;
|
||||
private readonly TopicId outputTopic;
|
||||
|
@ -69,6 +66,7 @@ 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:
|
||||
|
@ -96,24 +94,5 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
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;
|
||||
|
||||
|
@ -80,8 +78,7 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
|
|||
|
||||
private GroupChatOptions GroupChatOptions { get; }
|
||||
|
||||
private readonly RuntimeLayer runtimeLayer;
|
||||
|
||||
private readonly List<AgentMessage> messageThread = new();
|
||||
private Dictionary<string, AgentChatConfig> Participants { get; } = new();
|
||||
|
||||
protected GroupChatBase(List<IChatAgent> participants, ITerminationCondition? terminationCondition = null, int? maxTurns = null)
|
||||
|
@ -99,10 +96,9 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
|
|||
this.GroupChatOptions.Participants[participant.Name] = new GroupParticipant(config.ParticipantTopicType, participant.Description);
|
||||
}
|
||||
|
||||
this.TeamId = Guid.NewGuid().ToString().ToLowerInvariant();
|
||||
this.messageThread = new List<AgentMessage>(); // TODO: Allow injecting this
|
||||
|
||||
this.runtimeLayer = new RuntimeLayer(this);
|
||||
this.RunManager = new(this.InitializationLayersInternal);
|
||||
this.TeamId = Guid.NewGuid().ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
public string TeamId
|
||||
|
@ -118,7 +114,7 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
|
|||
if (Activator.CreateInstance(typeof(TManager), options) is TManager result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
|
@ -132,58 +128,17 @@ 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");
|
||||
}
|
||||
|
||||
private sealed class RuntimeLayer(GroupChatBase<TManager> groupChat) : IRunContextLayer
|
||||
// TODO: Turn this into an IDisposable-based utility
|
||||
private int running; // = 0
|
||||
private bool EnsureSingleRun()
|
||||
{
|
||||
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();
|
||||
}
|
||||
return Interlocked.CompareExchange(ref running, 1, 0) == 0;
|
||||
}
|
||||
|
||||
private IRunContextLayer[] InitializationLayersInternal =>
|
||||
[
|
||||
this.runtimeLayer, ..this.InitializationLayers
|
||||
];
|
||||
|
||||
protected virtual IEnumerable<IRunContextLayer> InitializationLayers => [];
|
||||
|
||||
private RunManager RunManager { get; }
|
||||
private void EndRun()
|
||||
{
|
||||
this.running = 0;
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -202,149 +157,79 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat
|
|||
return this.StreamAsync(taskStart, cancellationToken);
|
||||
}
|
||||
|
||||
private InProcessRuntime? Runtime => this.runtimeLayer.Runtime;
|
||||
private OutputSink? OutputSink => this.runtimeLayer.OutputSink;
|
||||
|
||||
private Task ShutdownTask
|
||||
public ValueTask ResetAsync(CancellationToken cancel)
|
||||
{
|
||||
get => this.runtimeLayer.ShutdownTask;
|
||||
set => this.runtimeLayer.ShutdownTask = value;
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
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)
|
||||
public async IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (task == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(task));
|
||||
}
|
||||
|
||||
const string TaskAlreadyRunning = "The task is already running";
|
||||
return this.RunManager.StreamAsync(
|
||||
this.StreamOutput,
|
||||
cancellationToken,
|
||||
this.PrepareStream(task),
|
||||
TaskAlreadyRunning);
|
||||
}
|
||||
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;
|
||||
|
||||
private async ValueTask ResetInternalAsync(CancellationToken cancel)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var participant in this.Participants.Values)
|
||||
// TODO: Protos
|
||||
GroupChatStart taskMessage = new GroupChatStart
|
||||
{
|
||||
await this.Runtime!.SendMessageAsync(
|
||||
new GroupChatReset(),
|
||||
new AgentId(participant.ParticipantTopicType, this.TeamId),
|
||||
cancellationToken: cancel);
|
||||
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(GroupChatManagerTopicType, this.TeamId),
|
||||
cancellationToken: cancel);
|
||||
|
||||
await this.Runtime!.RunUntilIdleAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.OutputSink?.Reset();
|
||||
}
|
||||
}
|
||||
this.EndRun();
|
||||
|
||||
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();
|
||||
await shutdownTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// 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;
|
||||
|
@ -10,17 +9,16 @@ 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>, ISaveState
|
||||
internal interface IGroupChatHandler : IHandle<GroupChatStart>, IHandle<GroupChatAgentResponse>, IHandle<object>
|
||||
{
|
||||
public void AttachMessagePublishServicer(MessagePublishServicer? servicer = null);
|
||||
public void DetachMessagePublishServicer() => this.AttachMessagePublishServicer(null);
|
||||
void AttachMessagePublishServicer(MessagePublishServicer? servicer = null);
|
||||
void DetachMessagePublishServicer() => this.AttachMessagePublishServicer(null);
|
||||
}
|
||||
|
||||
internal sealed class GroupChatHandlerRouter<TManager> : HostableAgentAdapter,
|
||||
IHandle<GroupChatStart>,
|
||||
IHandle<GroupChatAgentResponse>,
|
||||
IHandle<object>,
|
||||
ISaveState
|
||||
IHandle<object>
|
||||
|
||||
where TManager : GroupChatManagerBase, IGroupChatHandler
|
||||
{
|
||||
|
@ -47,10 +45,4 @@ 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);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public abstract class GroupChatManagerBase : IGroupChatHandler
|
|||
protected ITerminationCondition? TerminationCondition => this.options.TerminationCondition;
|
||||
protected int? MaxTurns => this.options.MaxTurns;
|
||||
|
||||
protected int CurrentTurn { get; set; }
|
||||
private 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 NotImplementedException();
|
||||
throw new InvalidOperationException($"Unhandled message in group chat manager: {item.GetType()}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions;
|
|||
|
||||
internal interface IOutputCollectionSink
|
||||
{
|
||||
public void CollectMessage(AgentMessage message);
|
||||
public void Terminate(StopMessage message);
|
||||
void CollectMessage(AgentMessage message);
|
||||
void Terminate(StopMessage message);
|
||||
}
|
||||
|
||||
internal sealed class OutputSink : IOutputCollectionSink
|
||||
|
@ -26,7 +26,7 @@ internal sealed class OutputSink : IOutputCollectionSink
|
|||
}
|
||||
|
||||
private readonly object sync = new();
|
||||
private SemaphoreSlim semapohre = new SemaphoreSlim(0, 1);
|
||||
private SemaphoreSlim semapohre = new SemaphoreSlim(1, 1);
|
||||
|
||||
private SinkFrame? receivingSinkFrame;
|
||||
|
||||
|
@ -43,12 +43,7 @@ internal sealed class OutputSink : IOutputCollectionSink
|
|||
frameAction(this.receivingSinkFrame);
|
||||
}
|
||||
|
||||
// TODO: Replace the Semaphore with a TaskSource approach
|
||||
try
|
||||
{
|
||||
semapohre.Release();
|
||||
}
|
||||
catch (SemaphoreFullException) { }
|
||||
semapohre.Release();
|
||||
}
|
||||
|
||||
public void CollectMessage(AgentMessage message)
|
||||
|
@ -87,14 +82,6 @@ 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
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
// 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, ISaveState
|
||||
public class RoundRobinGroupChatManager : GroupChatManagerBase
|
||||
{
|
||||
private readonly List<string> participantTopicTypes;
|
||||
private int nextSpeakerIndex;
|
||||
|
@ -31,29 +28,6 @@ public class RoundRobinGroupChatManager : GroupChatManagerBase, ISaveState
|
|||
|
||||
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>
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// 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?
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// 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; }
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// 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; }
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
**/.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
|
|
@ -1,34 +0,0 @@
|
|||
# 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"]
|
|
@ -6,16 +6,7 @@
|
|||
<ContainerRepository>autogen-host</ContainerRepository>
|
||||
<ContainerFamily>alpine</ContainerFamily>
|
||||
<EnableSdkContainerSupport>true</EnableSdkContainerSupport>
|
||||
|
||||
<PackAsTool>true</PackAsTool>
|
||||
<ToolCommandName>agenthost</ToolCommandName>
|
||||
<PackageOutputPath>./nupkg</PackageOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
<Import Project="$(RepoRoot)/nuget/nuget-package.props" />
|
||||
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ContainerEnvironmentVariable Include="ASPNETCORE_HTTP_PORTS" Value="5001" />
|
||||
<ContainerPort Include="5001" Type="tcp" />
|
||||
|
|
|
@ -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<ChatResponse> CompleteAsync(
|
||||
private Task<ChatCompletion> CompleteAsync(
|
||||
IList<ChatMessage> chatMessages,
|
||||
ChatOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ChatClient.GetResponseAsync(chatMessages, options, cancellationToken);
|
||||
return ChatClient.CompleteAsync(chatMessages, options, cancellationToken);
|
||||
}
|
||||
private IAsyncEnumerable<ChatResponseUpdate> CompleteStreamingAsync(
|
||||
private IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
|
||||
IList<ChatMessage> chatMessages,
|
||||
ChatOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ChatClient.GetStreamingResponseAsync(chatMessages, options, cancellationToken);
|
||||
return ChatClient.CompleteStreamingAsync(chatMessages, options, cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// IHandleConsole.cs
|
||||
using Google.Protobuf;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
|
||||
namespace Microsoft.AutoGen.Agents;
|
||||
|
@ -13,12 +14,13 @@ public interface IHandleConsole : IHandle<Output>, IHandle<Input>, IProcessIO
|
|||
/// <summary>
|
||||
/// Prototype for Publish Message Async method
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="topic"></param>
|
||||
/// <param name="messageId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>ValueTask</returns>
|
||||
ValueTask PublishMessageAsync(object message, TopicId topic, string? messageId = null, CancellationToken cancellationToken = default);
|
||||
ValueTask PublishMessageAsync<T>(T message, TopicId topic, string? messageId, CancellationToken token = default) where T : IMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Receives events of type Output and writes them to the console
|
||||
|
@ -37,7 +39,7 @@ public interface IHandleConsole : IHandle<Output>, IHandle<Input>, IProcessIO
|
|||
{
|
||||
Route = "console"
|
||||
};
|
||||
await PublishMessageAsync(evt, new TopicId("OutputWritten"), null, cancellationToken: CancellationToken.None).ConfigureAwait(false);
|
||||
await PublishMessageAsync(evt, new TopicId("OutputWritten"), null, token: CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -58,6 +60,6 @@ public interface IHandleConsole : IHandle<Output>, IHandle<Input>, IProcessIO
|
|||
{
|
||||
Route = "console"
|
||||
};
|
||||
await PublishMessageAsync(evt, new TopicId("InputProcessed"), null, cancellationToken: CancellationToken.None).ConfigureAwait(false);
|
||||
await PublishMessageAsync(evt, new TopicId("InputProcessed"), null, token: CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// IHandleFileIO.cs
|
||||
|
||||
using Google.Protobuf;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -23,12 +25,13 @@ public interface IHandleFileIO : IHandle<Input>, IHandle<Output>, IProcessIO
|
|||
/// <summary>
|
||||
/// Prototype for Publish Message Async method
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="topic"></param>
|
||||
/// <param name="messageId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>ValueTask</returns>
|
||||
ValueTask PublishMessageAsync(object message, TopicId topic, string? messageId = null, CancellationToken cancellationToken = default);
|
||||
ValueTask PublishMessageAsync<T>(T message, TopicId topic, string? messageId, CancellationToken token = default) where T : IMessage;
|
||||
async ValueTask IHandle<Input>.HandleAsync(Input item, MessageContext messageContext)
|
||||
{
|
||||
|
||||
|
@ -42,7 +45,7 @@ public interface IHandleFileIO : IHandle<Input>, IHandle<Output>, IProcessIO
|
|||
{
|
||||
Message = errorMessage
|
||||
};
|
||||
await PublishMessageAsync(err, new TopicId("IOError"), null, cancellationToken: CancellationToken.None).ConfigureAwait(false);
|
||||
await PublishMessageAsync(err, new TopicId("IOError"), null, token: CancellationToken.None).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
string content;
|
||||
|
@ -55,7 +58,7 @@ public interface IHandleFileIO : IHandle<Input>, IHandle<Output>, IProcessIO
|
|||
{
|
||||
Route = Route
|
||||
};
|
||||
await PublishMessageAsync(evt, new TopicId("InputProcessed"), null, cancellationToken: CancellationToken.None).ConfigureAwait(false);
|
||||
await PublishMessageAsync(evt, new TopicId("InputProcessed"), null, token: CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
async ValueTask IHandle<Output>.HandleAsync(Output item, MessageContext messageContext)
|
||||
{
|
||||
|
@ -67,6 +70,6 @@ public interface IHandleFileIO : IHandle<Input>, IHandle<Output>, IProcessIO
|
|||
{
|
||||
Route = Route
|
||||
};
|
||||
await PublishMessageAsync(evt, new TopicId("OutputWritten"), null, cancellationToken: CancellationToken.None).ConfigureAwait(false);
|
||||
await PublishMessageAsync(evt, new TopicId("OutputWritten"), null, token: CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
<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>
|
||||
|
|
|
@ -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
|
||||
public interface IAgent : ISaveState<IAgent>
|
||||
{
|
||||
/// <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();
|
||||
public ValueTask CloseAsync() => ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
public interface IAgentRuntime : ISaveState<IAgentRuntime>
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a message to an agent and gets a response.
|
||||
|
|
|
@ -9,10 +9,9 @@ namespace Microsoft.AutoGen.Contracts;
|
|||
/// Defines a contract for saving and loading the state of an object.
|
||||
/// The state must be JSON serializable.
|
||||
/// </summary>
|
||||
public interface ISaveState
|
||||
/// <typeparam name="T">The type of the object implementing this interface.</typeparam>
|
||||
public interface ISaveState<T>
|
||||
{
|
||||
public static ValueTask<JsonElement> DefaultSaveStateAsync() => new(JsonDocument.Parse("{}").RootElement);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current state of the object.
|
||||
/// </summary>
|
||||
|
@ -21,7 +20,7 @@ public interface ISaveState
|
|||
/// containing the saved state. The structure of the state is implementation-defined
|
||||
/// but must be JSON serializable.
|
||||
/// </returns>
|
||||
public virtual ValueTask<JsonElement> SaveStateAsync() => DefaultSaveStateAsync();
|
||||
public ValueTask<JsonElement> SaveStateAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Loads a previously saved state into the object.
|
||||
|
@ -31,6 +30,6 @@ public interface ISaveState
|
|||
/// is implementation-defined but must be JSON serializable.
|
||||
/// </param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public virtual ValueTask LoadStateAsync(JsonElement state) => ValueTask.CompletedTask;
|
||||
public ValueTask LoadStateAsync(JsonElement state);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
// 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);
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// AgentExtensions.cs
|
||||
|
||||
using System.Reflection;
|
||||
using Google.Protobuf;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
using Microsoft.AutoGen.Core.Grpc;
|
||||
|
||||
namespace Microsoft.AutoGen.Core;
|
||||
|
||||
internal static partial class AgentExtensions
|
||||
{
|
||||
private static readonly Type ProtobufIMessage = typeof(IMessage<>);
|
||||
private static bool IsProtobufType(this Type type)
|
||||
{
|
||||
// TODO: Support the non-generic IMessage as well
|
||||
Type specializedIMessageType = ProtobufIMessage.MakeGenericType(type);
|
||||
|
||||
// type T needs to derive from IMessage<T>
|
||||
return specializedIMessageType.IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public static void RegisterHandledMessageTypes(this IHostableAgent agent, IProtoSerializationRegistry registry)
|
||||
{
|
||||
Type agentRuntimeType = agent.GetType();
|
||||
|
||||
MethodInfo[] messageHandlers = agentRuntimeType.GetHandlers();
|
||||
|
||||
foreach (MethodInfo handler in messageHandlers)
|
||||
{
|
||||
Type messageType = handler.GetParameters().First().ParameterType;
|
||||
if (messageType.IsProtobufType() && registry.GetSerializer(messageType) == null)
|
||||
{
|
||||
registry.RegisterSerializer(messageType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ using Microsoft.AutoGen.Protobuf;
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
namespace Microsoft.AutoGen.Core.Grpc;
|
||||
|
||||
|
@ -18,7 +17,7 @@ public static class AgentsAppBuilderExtensions
|
|||
private const string _defaultAgentServiceAddress = "http://localhost:53071";
|
||||
|
||||
// TODO: How do we ensure AddGrpcAgentWorker and UseInProcessRuntime are mutually exclusive?
|
||||
public static AgentsAppBuilder AddGrpcAgentWorker(this AgentsAppBuilder builder, string? agentServiceAddress = null, bool useStrictDeserialiation = false)
|
||||
public static AgentsAppBuilder AddGrpcAgentWorker(this AgentsAppBuilder builder, string? agentServiceAddress = null)
|
||||
{
|
||||
builder.Services.AddGrpcClient<AgentRpc.AgentRpcClient>(options =>
|
||||
{
|
||||
|
@ -66,16 +65,7 @@ public static class AgentsAppBuilderExtensions
|
|||
});
|
||||
|
||||
builder.Services.TryAddSingleton(DistributedContextPropagator.Current);
|
||||
builder.Services.AddSingleton<IAgentRuntime, GrpcAgentRuntime>(
|
||||
(services) =>
|
||||
{
|
||||
return new GrpcAgentRuntime(
|
||||
services.GetRequiredService<AgentRpc.AgentRpcClient>(),
|
||||
services.GetRequiredService<IHostApplicationLifetime>(),
|
||||
services,
|
||||
services.GetRequiredService<ILogger<GrpcAgentRuntime>>(),
|
||||
useStrictDeserialiation);
|
||||
});
|
||||
builder.Services.AddSingleton<IAgentRuntime, GrpcAgentRuntime>();
|
||||
builder.Services.AddHostedService<GrpcAgentRuntime>(services =>
|
||||
{
|
||||
return (services.GetRequiredService<IAgentRuntime>() as GrpcAgentRuntime)!;
|
||||
|
|
|
@ -11,10 +11,9 @@ using Microsoft.Extensions.Logging;
|
|||
|
||||
namespace Microsoft.AutoGen.Core.Grpc;
|
||||
|
||||
internal sealed class AgentsContainer(IAgentRuntime hostingRuntime, IProtoSerializationRegistry serializationRegistry)
|
||||
internal sealed class AgentsContainer(IAgentRuntime hostingRuntime)
|
||||
{
|
||||
private readonly IAgentRuntime hostingRuntime = hostingRuntime;
|
||||
private readonly IProtoSerializationRegistry serializationRegistry = serializationRegistry;
|
||||
|
||||
private Dictionary<Contracts.AgentId, IHostableAgent> agentInstances = new();
|
||||
public Dictionary<string, ISubscriptionDefinition> Subscriptions = new();
|
||||
|
@ -30,10 +29,6 @@ internal sealed class AgentsContainer(IAgentRuntime hostingRuntime, IProtoSerial
|
|||
}
|
||||
|
||||
agent = await factoryFunc(agentId, this.hostingRuntime);
|
||||
|
||||
// Just-in-Time register the message types so we can deserialize them
|
||||
agent.RegisterHandledMessageTypes(this.serializationRegistry);
|
||||
|
||||
this.agentInstances.Add(agentId, agent);
|
||||
}
|
||||
|
||||
|
@ -90,16 +85,14 @@ public sealed class GrpcAgentRuntime : IHostedService, IAgentRuntime, IMessageSi
|
|||
public GrpcAgentRuntime(AgentRpc.AgentRpcClient client,
|
||||
IHostApplicationLifetime hostApplicationLifetime,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<GrpcAgentRuntime> logger,
|
||||
bool strictMessageDeserialization = false)
|
||||
ILogger<GrpcAgentRuntime> logger)
|
||||
{
|
||||
this._client = client;
|
||||
this._logger = logger;
|
||||
this._shutdownCts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping);
|
||||
|
||||
this._messageRouter = new GrpcMessageRouter(client, this, _clientId, logger, this._shutdownCts.Token);
|
||||
this._agentsContainer = new AgentsContainer(this, this.SerializationRegistry);
|
||||
this._strictMessageDeserialization = strictMessageDeserialization;
|
||||
this._agentsContainer = new AgentsContainer(this);
|
||||
|
||||
this.ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
@ -130,7 +123,6 @@ public sealed class GrpcAgentRuntime : IHostedService, IAgentRuntime, IMessageSi
|
|||
}
|
||||
}
|
||||
|
||||
private readonly bool _strictMessageDeserialization;
|
||||
public IProtoSerializationRegistry SerializationRegistry { get; } = new ProtobufSerializationRegistry();
|
||||
|
||||
public void Dispose()
|
||||
|
@ -239,6 +231,8 @@ public sealed class GrpcAgentRuntime : IHostedService, IAgentRuntime, IMessageSi
|
|||
|
||||
var messageId = evt.Id;
|
||||
var typeName = evt.Attributes[Constants.DATA_SCHEMA_ATTR].CeString;
|
||||
var serializer = SerializationRegistry.GetSerializer(typeName) ?? throw new Exception();
|
||||
var message = serializer.Deserialize(evt.ProtoData);
|
||||
|
||||
var messageContext = new MessageContext(messageId, cancellationToken)
|
||||
{
|
||||
|
@ -247,10 +241,6 @@ public sealed class GrpcAgentRuntime : IHostedService, IAgentRuntime, IMessageSi
|
|||
IsRpc = false
|
||||
};
|
||||
|
||||
// We may not have a Serializer registered yet, if this is the first time we are instantiating the agent
|
||||
IProtobufMessageSerializer? serializer = SerializationRegistry.GetSerializer(typeName);
|
||||
object? message = serializer?.Deserialize(evt.ProtoData);
|
||||
|
||||
// Iterate over subscriptions values to find receiving agents
|
||||
foreach (var subscription in this._agentsContainer.Subscriptions.Values)
|
||||
{
|
||||
|
@ -258,55 +248,17 @@ public sealed class GrpcAgentRuntime : IHostedService, IAgentRuntime, IMessageSi
|
|||
{
|
||||
var recipient = subscription.MapToAgent(topic);
|
||||
var agent = await this._agentsContainer.EnsureAgentAsync(recipient);
|
||||
|
||||
// give the serializer a second chance to have been registered
|
||||
serializer ??= SerializationRegistry.GetSerializer(typeName);
|
||||
|
||||
if (serializer != null)
|
||||
{
|
||||
message ??= serializer.Deserialize(evt.ProtoData);
|
||||
await agent.OnMessageAsync(message, messageContext);
|
||||
}
|
||||
else if (_strictMessageDeserialization)
|
||||
{
|
||||
throw new Exception($"Could not find a serializer for message of type {typeName}");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Could not find a serializer for message of type {typeName}; this is likely due there not yet being an instantiated agent with a contract for it.");
|
||||
}
|
||||
await agent.OnMessageAsync(message, messageContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask StartAsync(CancellationToken cancellationToken)
|
||||
public ValueTask StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await this._messageRouter.StartAsync(cancellationToken);
|
||||
if (this._agentsContainer.RegisteredAgentTypes.Count > 0)
|
||||
{
|
||||
foreach (var type in this._agentsContainer.RegisteredAgentTypes)
|
||||
{
|
||||
await this._client.RegisterAgentAsync(new RegisterAgentTypeRequest
|
||||
{
|
||||
Type = type
|
||||
}, this.CallOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._agentsContainer.Subscriptions.Count > 0)
|
||||
{
|
||||
foreach (var subscription in this._agentsContainer.Subscriptions.Values)
|
||||
{
|
||||
await this._client.AddSubscriptionAsync(new AddSubscriptionRequest
|
||||
{
|
||||
Subscription = subscription.ToProtobuf()
|
||||
}, this.CallOptions);
|
||||
}
|
||||
}
|
||||
return this._messageRouter.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
Task IHostedService.StartAsync(CancellationToken cancellationToken) => this.StartAsync(cancellationToken).AsTask();
|
||||
Task IHostedService.StartAsync(CancellationToken cancellationToken) => this._messageRouter.StartAsync(cancellationToken).AsTask();
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -392,39 +344,30 @@ public sealed class GrpcAgentRuntime : IHostedService, IAgentRuntime, IMessageSi
|
|||
{
|
||||
this._agentsContainer.AddSubscription(subscription);
|
||||
|
||||
if (this._messageRouter.IsChannelOpen)
|
||||
var _ = await this._client.AddSubscriptionAsync(new AddSubscriptionRequest
|
||||
{
|
||||
var _ = await this._client.AddSubscriptionAsync(new AddSubscriptionRequest
|
||||
{
|
||||
Subscription = subscription.ToProtobuf()
|
||||
}, this.CallOptions);
|
||||
}
|
||||
Subscription = subscription.ToProtobuf()
|
||||
}, this.CallOptions);
|
||||
}
|
||||
|
||||
public async ValueTask RemoveSubscriptionAsync(string subscriptionId)
|
||||
{
|
||||
this._agentsContainer.RemoveSubscriptionAsync(subscriptionId);
|
||||
|
||||
if (this._messageRouter.IsChannelOpen)
|
||||
await this._client.RemoveSubscriptionAsync(new RemoveSubscriptionRequest
|
||||
{
|
||||
await this._client.RemoveSubscriptionAsync(new RemoveSubscriptionRequest
|
||||
{
|
||||
Id = subscriptionId
|
||||
}, this.CallOptions);
|
||||
}
|
||||
Id = subscriptionId
|
||||
}, this.CallOptions);
|
||||
}
|
||||
|
||||
public async ValueTask<AgentType> RegisterAgentFactoryAsync(AgentType type, Func<Contracts.AgentId, IAgentRuntime, ValueTask<IHostableAgent>> factoryFunc)
|
||||
{
|
||||
this._agentsContainer.RegisterAgentFactory(type, factoryFunc);
|
||||
|
||||
if (this._messageRouter.IsChannelOpen)
|
||||
await this._client.RegisterAgentAsync(new RegisterAgentTypeRequest
|
||||
{
|
||||
await this._client.RegisterAgentAsync(new RegisterAgentTypeRequest
|
||||
{
|
||||
Type = type,
|
||||
}, this.CallOptions);
|
||||
}
|
||||
Type = type,
|
||||
}, this.CallOptions);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -34,8 +34,6 @@ internal sealed class AutoRestartChannel : IDisposable
|
|||
_shutdownCts = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellation);
|
||||
}
|
||||
|
||||
public bool Connected { get => _channel is not null; }
|
||||
|
||||
public void EnsureConnected()
|
||||
{
|
||||
_logger.LogInformation("Connecting to gRPC endpoint " + Environment.GetEnvironmentVariable("AGENT_HOST"));
|
||||
|
@ -43,7 +41,7 @@ internal sealed class AutoRestartChannel : IDisposable
|
|||
if (this.RecreateChannel(null) == null)
|
||||
{
|
||||
throw new Exception("Failed to connect to gRPC endpoint.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public AsyncDuplexStreamingCall<Message, Message> StreamingCall
|
||||
|
@ -117,9 +115,8 @@ internal sealed class GrpcMessageRouter(AgentRpc.AgentRpcClient client,
|
|||
private readonly CancellationTokenSource _shutdownCts = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellation);
|
||||
|
||||
private readonly IMessageSink<Message> _incomingMessageSink = incomingMessageSink;
|
||||
|
||||
// TODO: Enable a way to configure the channel options
|
||||
private readonly Channel<(Message Message, TaskCompletionSource WriteCompletionSource)> _outboundMessagesChannel
|
||||
// TODO: Enable a way to configure the channel options
|
||||
= Channel.CreateBounded<(Message, TaskCompletionSource)>(DefaultChannelOptions);
|
||||
|
||||
private readonly AutoRestartChannel _incomingMessageChannel = new AutoRestartChannel(client, clientId, logger, shutdownCancellation);
|
||||
|
@ -290,8 +287,6 @@ internal sealed class GrpcMessageRouter(AgentRpc.AgentRpcClient client,
|
|||
this._incomingMessageChannel.Dispose();
|
||||
}
|
||||
|
||||
public bool IsChannelOpen => this._incomingMessageChannel.Connected;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_outboundMessagesChannel.Writer.TryComplete();
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/nuget/nuget-package.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Microsoft.AutoGen.Core.csproj" />
|
||||
<ProjectReference Include="..\Contracts\Microsoft.AutoGen.Contracts.csproj" />
|
||||
|
|
|
@ -28,7 +28,10 @@ public class ProtobufSerializationRegistry : IProtoSerializationRegistry
|
|||
|
||||
public void RegisterSerializer(Type type, IProtobufMessageSerializer serializer)
|
||||
{
|
||||
_serializers.TryAdd(TypeNameResolver.ResolveTypeName(type), serializer);
|
||||
if (_serializers.ContainsKey(TypeNameResolver.ResolveTypeName(type)))
|
||||
{
|
||||
throw new InvalidOperationException($"Serializer already registered for {type.FullName}");
|
||||
}
|
||||
_serializers[TypeNameResolver.ResolveTypeName(type)] = serializer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AutoGen.Contracts;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -12,7 +13,7 @@ namespace Microsoft.AutoGen.Core;
|
|||
/// <summary>
|
||||
/// Represents the base class for an agent in the AutoGen system.
|
||||
/// </summary>
|
||||
public abstract class BaseAgent : IHostableAgent, ISaveState
|
||||
public abstract class BaseAgent : IAgent, IHostableAgent
|
||||
{
|
||||
/// <summary>
|
||||
/// The activity source for tracing.
|
||||
|
@ -92,6 +93,15 @@ public abstract class BaseAgent : IHostableAgent, ISaveState
|
|||
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);
|
||||
|
@ -102,19 +112,4 @@ public abstract class BaseAgent : IHostableAgent, ISaveState
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
public bool DeliverToSelf { get; set; } //= false;
|
||||
|
||||
internal Dictionary<AgentId, IHostableAgent> agentInstances = new();
|
||||
private Dictionary<string, ISubscriptionDefinition> subscriptions = new();
|
||||
private Dictionary<AgentType, Func<AgentId, IAgentRuntime, ValueTask<IHostableAgent>>> agentFactories = new();
|
||||
Dictionary<string, ISubscriptionDefinition> subscriptions = new();
|
||||
Dictionary<AgentType, Func<AgentId, IAgentRuntime, ValueTask<IHostableAgent>>> agentFactories = new();
|
||||
|
||||
private ValueTask<T> ExecuteTracedAsync<T>(Func<ValueTask<T>> func)
|
||||
{
|
||||
|
@ -43,45 +43,28 @@ 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)))
|
||||
{
|
||||
try
|
||||
AgentId? sender = envelope.Sender;
|
||||
|
||||
CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(envelope.Cancellation, deliveryToken);
|
||||
MessageContext messageContext = new(envelope.MessageId, combinedSource.Token)
|
||||
{
|
||||
deliveryToken.ThrowIfCancellationRequested();
|
||||
Sender = sender,
|
||||
Topic = topic,
|
||||
IsRpc = false
|
||||
};
|
||||
|
||||
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)
|
||||
AgentId agentId = subscription.MapToAgent(topic);
|
||||
if (!this.DeliverToSelf && sender.HasValue && sender == agentId)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Count > 0)
|
||||
{
|
||||
// TODO: Unwrap TargetInvocationException?
|
||||
throw new AggregateException("One or more exceptions occurred while processing the message.", exceptions);
|
||||
IHostableAgent agent = await this.EnsureAgentAsync(agentId);
|
||||
|
||||
// TODO: Cancellation propagation!
|
||||
await agent.OnMessageAsync(envelope.Message, messageContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +78,7 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
|
||||
this.messageDeliveryQueue.Enqueue(delivery);
|
||||
|
||||
return delivery.FutureNoResult;
|
||||
return ValueTask.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,15 +104,14 @@ 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(async () =>
|
||||
return this.ExecuteTracedAsync(() =>
|
||||
{
|
||||
MessageDelivery delivery = new MessageEnvelope(message, messageId, cancellationToken)
|
||||
.WithSender(sender)
|
||||
.ForSend(recepient, this.SendMessageServicer);
|
||||
|
||||
this.messageDeliveryQueue.Enqueue(delivery);
|
||||
|
||||
return await delivery.Future;
|
||||
return delivery.Future;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -233,9 +215,7 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
var agentState = await this.agentInstances[agentId].SaveStateAsync();
|
||||
state[agentId.ToString()] = agentState;
|
||||
}
|
||||
|
||||
JsonElement jsonElement = JsonSerializer.SerializeToElement(state);
|
||||
return jsonElement;
|
||||
return JsonSerializer.SerializeToElement(state);
|
||||
}
|
||||
|
||||
public ValueTask<AgentType> RegisterAgentFactoryAsync<TAgent>(AgentType type, Func<AgentId, IAgentRuntime, ValueTask<TAgent>> factoryFunc) where TAgent : IHostableAgent
|
||||
|
@ -260,7 +240,7 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
return ValueTask.FromResult(new AgentProxy(agentId, this));
|
||||
}
|
||||
|
||||
public ValueTask ProcessNextMessageAsync(CancellationToken cancellation = default)
|
||||
public ValueTask ProcessNextMessage(CancellationToken cancellation = default)
|
||||
{
|
||||
Debug.WriteLine("Processing next message...");
|
||||
if (this.messageDeliveryQueue.TryDequeue(out MessageDelivery? delivery))
|
||||
|
@ -276,34 +256,11 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
private CancellationTokenSource? finishSource;
|
||||
private async Task RunAsync(CancellationToken cancellation)
|
||||
{
|
||||
Dictionary<Guid, Task> pendingTasks = new();
|
||||
while (!cancellation.IsCancellationRequested && shouldContinue())
|
||||
{
|
||||
// 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 this.ProcessNextMessage(cancellation);
|
||||
}
|
||||
|
||||
await Task.WhenAll(pendingTasks.Values.Where(t => t is not null).ToArray());
|
||||
await this.FinishAsync(this.finishSource?.Token ?? CancellationToken.None);
|
||||
}
|
||||
|
||||
|
@ -343,15 +300,12 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task RunUntilIdleAsync()
|
||||
public Task RunUntilIdleAsync()
|
||||
{
|
||||
Func<bool> oldShouldContinue = this.shouldContinue;
|
||||
this.shouldContinue = () => !this.messageDeliveryQueue.IsEmpty;
|
||||
|
||||
// TODO: Do we want detach semantics?
|
||||
await this.messageDeliveryTask;
|
||||
|
||||
this.shouldContinue = oldShouldContinue;
|
||||
return this.messageDeliveryTask;
|
||||
}
|
||||
|
||||
private async Task FinishAsync(CancellationToken token)
|
||||
|
@ -363,9 +317,6 @@ public sealed class InProcessRuntime : IAgentRuntime, IHostedService
|
|||
await agent.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
this.shutdownSource = null;
|
||||
this.finishSource = null;
|
||||
}
|
||||
|
||||
Task IHostedService.StartAsync(CancellationToken cancellationToken) => this.StartAsync(cancellationToken).AsTask();
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// 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)
|
||||
internal sealed class MessageDelivery(MessageEnvelope message, Func<MessageEnvelope, CancellationToken, ValueTask> servicer, IResultSink<object?>? resultSink = null)
|
||||
{
|
||||
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.Future;
|
||||
public ValueTask FutureNoResult => this.ResultSink.FutureNoResult;
|
||||
public ValueTask<object?> Future => this.ResultSink != null ? this.ResultSink.Future : ValueTask.FromResult((object?)null);
|
||||
|
||||
public ValueTask InvokeAsync(CancellationToken cancellation)
|
||||
{
|
||||
|
@ -50,19 +48,8 @@ internal sealed class MessageEnvelope
|
|||
ResultSink<object?> resultSink = new ResultSink<object?>();
|
||||
Func<MessageEnvelope, CancellationToken, ValueTask> boundServicer = async (envelope, cancellation) =>
|
||||
{
|
||||
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);
|
||||
}
|
||||
object? result = await servicer(envelope, cancellation);
|
||||
resultSink.SetResult(result);
|
||||
};
|
||||
|
||||
return new MessageDelivery(this, boundServicer, resultSink);
|
||||
|
@ -72,20 +59,6 @@ internal sealed class MessageEnvelope
|
|||
{
|
||||
this.Topic = topic;
|
||||
|
||||
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);
|
||||
return new MessageDelivery(this, servicer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,13 @@ using System.Threading.Tasks.Sources;
|
|||
|
||||
namespace Microsoft.AutoGen.Core;
|
||||
|
||||
internal interface IResultSink<TResult> : IValueTaskSource<TResult>, IValueTaskSource
|
||||
internal interface IResultSink<TResult> : IValueTaskSource<TResult>
|
||||
{
|
||||
public void SetResult(TResult result);
|
||||
public void SetException(Exception exception);
|
||||
public void SetCancelled(OperationCanceledException? ocEx = null);
|
||||
void SetResult(TResult result);
|
||||
void SetException(Exception exception);
|
||||
void SetCancelled();
|
||||
|
||||
public ValueTask<TResult> Future { get; }
|
||||
public ValueTask FutureNoResult { get; }
|
||||
ValueTask<TResult> Future { get; }
|
||||
}
|
||||
|
||||
public sealed class ResultSink<TResult> : IResultSink<TResult>
|
||||
|
@ -35,10 +34,10 @@ public sealed class ResultSink<TResult> : IResultSink<TResult>
|
|||
}
|
||||
|
||||
public bool IsCancelled { get; private set; }
|
||||
public void SetCancelled(OperationCanceledException? ocEx = null)
|
||||
public void SetCancelled()
|
||||
{
|
||||
this.IsCancelled = true;
|
||||
this.core.SetException(ocEx ?? new OperationCanceledException());
|
||||
this.core.SetException(new OperationCanceledException());
|
||||
}
|
||||
|
||||
public void SetException(Exception exception)
|
||||
|
@ -51,9 +50,5 @@ 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,13 +41,12 @@ public static class ServiceCollectionChatClientExtensions
|
|||
Func<ChatClientBuilder, ChatClientBuilder>? builder = null)
|
||||
{
|
||||
uri ??= new Uri("http://localhost:11434");
|
||||
services.AddChatClient(service =>
|
||||
return services.AddChatClient(pipeline =>
|
||||
{
|
||||
var httpClient = service.GetService<HttpClient>() ?? new();
|
||||
return new OllamaChatClient(uri, modelName, httpClient);
|
||||
builder?.Invoke(pipeline);
|
||||
var httpClient = pipeline.Services.GetService<HttpClient>() ?? new();
|
||||
return pipeline.Use(new OllamaChatClient(uri, modelName, httpClient));
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
public static IServiceCollection AddOpenAIChatClient(
|
||||
this IHostApplicationBuilder hostBuilder,
|
||||
|
@ -82,17 +81,16 @@ public static class ServiceCollectionChatClientExtensions
|
|||
Uri? endpoint = null,
|
||||
Func<ChatClientBuilder, ChatClientBuilder>? builder = null)
|
||||
{
|
||||
services
|
||||
return services
|
||||
.AddSingleton(_ => endpoint is null
|
||||
? new OpenAIClient(apiKey)
|
||||
: new AzureOpenAIClient(endpoint, new ApiKeyCredential(apiKey)))
|
||||
.AddChatClient(service =>
|
||||
.AddChatClient(pipeline =>
|
||||
{
|
||||
var openAiClient = service.GetRequiredService<OpenAIClient>();
|
||||
return openAiClient.AsChatClient(modelOrDeploymentName);
|
||||
builder?.Invoke(pipeline);
|
||||
var openAiClient = pipeline.Services.GetRequiredService<OpenAIClient>();
|
||||
return pipeline.Use(openAiClient.AsChatClient(modelOrDeploymentName));
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
public static IServiceCollection AddAzureChatClient(
|
||||
this IHostApplicationBuilder hostBuilder,
|
||||
|
@ -111,10 +109,12 @@ 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);
|
||||
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;
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,40 +4,12 @@ using System.Collections.Concurrent;
|
|||
using Microsoft.AutoGen.Protobuf;
|
||||
|
||||
namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Stores agent subscription information such as topic and prefix mappings,
|
||||
/// and maintains an ETag for concurrency checks.
|
||||
/// </summary>
|
||||
public class AgentsRegistryState
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps each agent ID to the set of topics they subscribe to.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, HashSet<string>> AgentsToTopicsMap { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Maps each agent ID to the set of topic prefixes they subscribe to.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, HashSet<string>> AgentsToTopicsPrefixMap { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Maps each topic name to the set of agent types subscribed to it.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, HashSet<string>> TopicToAgentTypesMap { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Maps each topic prefix to the set of agent types subscribed to it.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, HashSet<string>> TopicPrefixToAgentTypesMap { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Stores subscriptions by GUID
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, HashSet<Subscription>> GuidSubscriptionsMap { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The concurrency ETag for identifying the registry's version or state.
|
||||
/// </summary>
|
||||
public string Etag { get; set; } = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// IConnection.cs
|
||||
|
||||
namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Abstractions;
|
||||
public interface IConnection
|
||||
{
|
||||
}
|
|
@ -5,45 +5,11 @@ 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);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue