Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef5e84859d | ||
|
|
ae62a557d3 | ||
|
|
86de80a998 | ||
|
|
2bac2b3824 | ||
|
|
3185233233 | ||
|
|
48597bdf30 | ||
|
|
b53746dd1f | ||
|
|
ebbbf62df5 | ||
|
|
d0f40e7d35 | ||
|
|
15a4ec7e33 | ||
|
|
6c0dfae9bf | ||
|
|
48d15a6128 | ||
|
|
7ddbce89d0 | ||
|
|
cf07f732c2 | ||
|
|
897d3dc9ea | ||
|
|
8438e75dff | ||
|
|
a099575620 | ||
|
|
37c17c9e3d | ||
|
|
7e013787ed | ||
|
|
984b3b28ba | ||
|
|
80f04ada32 | ||
|
|
3dd6971fce | ||
|
|
02cbda22dd | ||
|
|
e3e40ede2b | ||
|
|
0c3d2fdbe2 | ||
|
|
6be09de87d | ||
|
|
1e00887167 | ||
|
|
705791d17f | ||
|
|
0ec9bbdc13 | ||
|
|
146c28ae27 | ||
|
|
fc61fb062e | ||
|
|
a46402fd08 | ||
|
|
47307a1045 | ||
|
|
b21e355ce1 | ||
|
|
0c69df107e | ||
|
|
4800807783 | ||
|
|
71ecc8f35b | ||
|
|
782c2aceff | ||
|
|
ff27fb157c |
53
.github/workflows/docker-image.yml
vendored
Normal file
53
.github/workflows/docker-image.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Docker Image CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "develop"]
|
||||
pull_request:
|
||||
branches: ["main", "develop"]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=raw,value=develop,enable=${{ github.ref == format('refs/heads/{0}', 'develop') }}
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||
type=sha
|
||||
type=ref,event=branch
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
69
CHANGELOG.md
Normal file
69
CHANGELOG.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.0.9] - 2025-05-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Enhance user authentication with detailed error handling
|
||||
|
||||
## [0.0.8] - 2025-05-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Update author information in multiple files
|
||||
|
||||
## [0.0.7] - 2025-05-13
|
||||
|
||||
### Added
|
||||
|
||||
- Docker image CI workflow for automated builds and pushes
|
||||
- GitHub Container Registry (GHCR) integration
|
||||
- Automated image tagging based on branch and commit
|
||||
- Docker Buildx setup for multi-platform builds
|
||||
- Cache optimization for faster builds
|
||||
- Automated image publishing on push to main and develop branches
|
||||
|
||||
## [0.0.6] - 2025-05-13
|
||||
|
||||
### Added
|
||||
|
||||
- Initial public release of Evo AI platform
|
||||
- FastAPI-based backend API
|
||||
- JWT authentication with email verification
|
||||
- Agent management (LLM, A2A, Sequential, Parallel, Loop, Workflow)
|
||||
- Agent 2 Agent (A2A) protocol support (Google A2A spec)
|
||||
- MCP server integration and management
|
||||
- Custom tools management for agents
|
||||
- Folder-based agent organization
|
||||
- Secure API key management with encryption
|
||||
- PostgreSQL and Redis integration
|
||||
- Email notifications (SendGrid) with Jinja2 templates
|
||||
- Audit log system for administrative actions
|
||||
- LangGraph integration for workflow agents
|
||||
- OpenTelemetry tracing and Langfuse integration
|
||||
- Docker and Docker Compose support
|
||||
- English documentation and codebase
|
||||
|
||||
### Changed
|
||||
|
||||
- N/A
|
||||
|
||||
### Fixed
|
||||
|
||||
- N/A
|
||||
|
||||
### Security
|
||||
|
||||
- JWT tokens with expiration and resource-based access control
|
||||
- Secure password hashing (bcrypt)
|
||||
- Account lockout after multiple failed login attempts
|
||||
- Email verification and password reset flows
|
||||
|
||||
---
|
||||
|
||||
Older versions and future releases will be listed here.
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2025 Evolution API
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
163
README.md
163
README.md
@@ -301,6 +301,51 @@ Authorization: Bearer your-token-jwt
|
||||
- **LangGraph**: Framework for building stateful, multi-agent workflows
|
||||
- **ReactFlow**: Library for building node-based visual workflows
|
||||
|
||||
## 📊 Langfuse Integration (Tracing & Observability)
|
||||
|
||||
Evo AI platform natively supports integration with [Langfuse](https://langfuse.com/) for detailed tracing of agent executions, prompts, model responses, and tool calls, using the OpenTelemetry (OTel) standard.
|
||||
|
||||
### Why use Langfuse?
|
||||
|
||||
- Visual dashboard for agent traces, prompts, and executions
|
||||
- Detailed analytics for debugging and evaluating LLM apps
|
||||
- Easy integration with Google ADK and other frameworks
|
||||
|
||||
### How it works
|
||||
|
||||
- Every agent execution (including streaming) is automatically traced via OpenTelemetry spans
|
||||
- Data is sent to Langfuse, where it can be visualized and analyzed
|
||||
|
||||
### How to configure
|
||||
|
||||
1. **Set environment variables in your `.env`:**
|
||||
|
||||
```env
|
||||
LANGFUSE_PUBLIC_KEY="pk-lf-..." # Your Langfuse public key
|
||||
LANGFUSE_SECRET_KEY="sk-lf-..." # Your Langfuse secret key
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel" # (or us.cloud... for US region)
|
||||
```
|
||||
|
||||
> **Attention:** Do not swap the keys! `pk-...` is public, `sk-...` is secret.
|
||||
|
||||
2. **Automatic initialization**
|
||||
|
||||
- Tracing is automatically initialized when the application starts (`src/main.py`).
|
||||
- Agent execution functions are already instrumented with spans (`src/services/agent_runner.py`).
|
||||
|
||||
3. **View in the Langfuse dashboard**
|
||||
- Access your Langfuse dashboard to see real-time traces.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- **401 Error (Invalid credentials):**
|
||||
- Check if the keys are correct and not swapped in your `.env`.
|
||||
- Make sure the endpoint matches your region (EU or US).
|
||||
- **Context error in async generator:**
|
||||
- The code is already adjusted to avoid OpenTelemetry context issues in async generators.
|
||||
- **Questions about integration:**
|
||||
- See the [official Langfuse documentation - Google ADK](https://langfuse.com/docs/integrations/google-adk)
|
||||
|
||||
## 🤖 Agent 2 Agent (A2A) Protocol Support
|
||||
|
||||
Evo AI implements the Google's Agent 2 Agent (A2A) protocol, enabling seamless communication and interoperability between AI agents. This implementation includes:
|
||||
@@ -381,16 +426,13 @@ Before starting, make sure you have the following installed:
|
||||
|
||||
You'll also need the following accounts/API keys:
|
||||
|
||||
- **OpenAI API Key**: Or API key from another AI provider
|
||||
- **SendGrid Account**: For email functionality
|
||||
- **Google API Key**: If using Google's A2A protocol implementation
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
- Python 3.10+
|
||||
- PostgreSQL
|
||||
- Redis
|
||||
- OpenAI API Key (or other AI provider)
|
||||
- SendGrid Account (for email sending)
|
||||
|
||||
## 🔧 Installation
|
||||
@@ -398,7 +440,7 @@ You'll also need the following accounts/API keys:
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-username/evo-ai.git
|
||||
git clone https://github.com/EvolutionAPI/evo-ai.git
|
||||
cd evo-ai
|
||||
```
|
||||
|
||||
@@ -446,6 +488,21 @@ make alembic-upgrade
|
||||
make seed-all
|
||||
```
|
||||
|
||||
## 🖥️ Frontend Installation
|
||||
|
||||
After installing Evo AI (the backend), you need to install the frontend to access the web interface:
|
||||
|
||||
1. Clone the frontend repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/EvolutionAPI/evo-ai-frontend.git
|
||||
cd evo-ai-frontend
|
||||
```
|
||||
|
||||
2. Follow the installation instructions in the frontend repository's README to set up and run the web interface.
|
||||
|
||||
> The backend (API) and frontend are separate projects. Make sure both are running for full platform functionality.
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
After installation, follow these steps to set up your first agent:
|
||||
@@ -590,54 +647,6 @@ make run-prod # For production with multiple workers
|
||||
|
||||
The API will be available at `http://localhost:8000`
|
||||
|
||||
## 📚 API Documentation
|
||||
|
||||
The interactive API documentation is available at:
|
||||
|
||||
- Swagger UI: `http://localhost:8000/docs`
|
||||
- ReDoc: `http://localhost:8000/redoc`
|
||||
|
||||
## 📊 Logs and Audit
|
||||
|
||||
- Logs are stored in the `logs/` directory with the following format:
|
||||
- `{logger_name}_{date}.log`
|
||||
- The system maintains audit logs for important administrative actions
|
||||
- Each action is recorded with information such as user, IP, date/time, and details
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions from the community! Here's how you can help:
|
||||
|
||||
1. Fork the project
|
||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Make your changes and add tests if possible
|
||||
4. Run tests and make sure they pass
|
||||
5. Commit your changes following conventional commits format (`feat: add amazing feature`)
|
||||
6. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||
7. Open a Pull Request
|
||||
|
||||
Please read our [Contributing Guidelines](CONTRIBUTING.md) for more details.
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## 📊 Stargazers
|
||||
|
||||
[](https://github.com/your-username/evo-ai/stargazers)
|
||||
|
||||
## 🔄 Forks
|
||||
|
||||
[](https://github.com/your-username/evo-ai/network/members)
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
- [FastAPI](https://fastapi.tiangolo.com/)
|
||||
- [SQLAlchemy](https://www.sqlalchemy.org/)
|
||||
- [Google ADK](https://github.com/google/adk)
|
||||
- [LangGraph](https://github.com/langchain-ai/langgraph)
|
||||
- [ReactFlow](https://reactflow.dev/)
|
||||
|
||||
## 👨💻 Development Commands
|
||||
|
||||
```bash
|
||||
@@ -891,3 +900,53 @@ GET /api/v1/agents?folder_id=folder-uuid
|
||||
Authorization: Bearer your-token-jwt
|
||||
x-client-id: client-uuid
|
||||
```
|
||||
|
||||
## 📚 API Documentation
|
||||
|
||||
The interactive API documentation is available at:
|
||||
|
||||
- Swagger UI: `http://localhost:8000/docs`
|
||||
- ReDoc: `http://localhost:8000/redoc`
|
||||
|
||||
## 📊 Logs and Audit
|
||||
|
||||
- Logs are stored in the `logs/` directory with the following format:
|
||||
- `{logger_name}_{date}.log`
|
||||
- The system maintains audit logs for important administrative actions
|
||||
- Each action is recorded with information such as user, IP, date/time, and details
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions from the community! Here's how you can help:
|
||||
|
||||
1. Fork the project
|
||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Make your changes and add tests if possible
|
||||
4. Run tests and make sure they pass
|
||||
5. Commit your changes following conventional commits format (`feat: add amazing feature`)
|
||||
6. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||
7. Open a Pull Request
|
||||
|
||||
Please read our [Contributing Guidelines](CONTRIBUTING.md) for more details.
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the [Apache License 2.0](./LICENSE).
|
||||
|
||||
The use of the name, logo, or trademark "Evolution API" is protected and not automatically granted by the license. See section 6 (Trademarks) of the license for details about trademark usage.
|
||||
|
||||
## 📊 Stargazers
|
||||
|
||||
[](https://github.com/EvolutionAPI/evo-ai/stargazers)
|
||||
|
||||
## 🔄 Forks
|
||||
|
||||
[](https://github.com/EvolutionAPI/evo-ai/network/members)
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
- [FastAPI](https://fastapi.tiangolo.com/)
|
||||
- [SQLAlchemy](https://www.sqlalchemy.org/)
|
||||
- [Google ADK](https://github.com/google/adk)
|
||||
- [LangGraph](https://github.com/langchain-ai/langgraph)
|
||||
- [ReactFlow](https://reactflow.dev/)
|
||||
|
||||
41
conftest.py
41
conftest.py
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: conftest.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
@@ -22,11 +51,11 @@ TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engin
|
||||
def db_session():
|
||||
"""Creates a fresh database session for each test."""
|
||||
Base.metadata.create_all(bind=engine) # Create tables
|
||||
|
||||
|
||||
connection = engine.connect()
|
||||
transaction = connection.begin()
|
||||
session = TestingSessionLocal(bind=connection)
|
||||
|
||||
|
||||
# Use our test database instead of the standard one
|
||||
def override_get_db():
|
||||
try:
|
||||
@@ -34,11 +63,11 @@ def db_session():
|
||||
session.commit()
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
|
||||
|
||||
yield session # The test will run here
|
||||
|
||||
|
||||
# Teardown
|
||||
transaction.rollback()
|
||||
connection.close()
|
||||
@@ -50,4 +79,4 @@ def db_session():
|
||||
def client(db_session):
|
||||
"""Creates a FastAPI TestClient with database session fixture."""
|
||||
with TestClient(app) as test_client:
|
||||
yield test_client
|
||||
yield test_client
|
||||
|
||||
@@ -8,14 +8,14 @@ version = "1.0.0"
|
||||
description = "API for executing AI agents"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{name = "EvoAI Team", email = "admin@evoai.com"}
|
||||
{name = "Davidson Gomes", email = "contato@evolution-api.com"}
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
license = {text = "Proprietary"}
|
||||
license = {text = "Apache-2.0"}
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"License :: Other/Proprietary License",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: run_seeders.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Main script to run all seeders in sequence.
|
||||
Checks dependencies between seeders and runs them in the correct order.
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: admin_seeder.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Script to create an initial admin user:
|
||||
- Email: admin@evoai.com
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: client_seeder.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Script to create a demo client:
|
||||
- Name: Demo Client
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: mcp_server_seeder.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Script to create default MCP servers:
|
||||
- Brave Search
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: tool_seeder.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Script to create default tools:
|
||||
-
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: a2a_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Routes for the A2A (Agent-to-Agent) protocol.
|
||||
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: admin_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: agent_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Header, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from src.config.database import get_db
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: auth_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from src.config.database import get_db
|
||||
@@ -54,8 +83,7 @@ async def register_user(user_data: UserCreate, db: Session = Depends(get_db)):
|
||||
Raises:
|
||||
HTTPException: If there is an error in registration
|
||||
"""
|
||||
# TODO: remover o auto_verify temporariamente para teste
|
||||
user, message = create_user(db, user_data, is_admin=False, auto_verify=True)
|
||||
user, message = create_user(db, user_data, is_admin=False, auto_verify=False)
|
||||
if not user:
|
||||
logger.error(f"Error registering user: {message}")
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
||||
@@ -163,14 +191,36 @@ async def login_for_access_token(form_data: UserLogin, db: Session = Depends(get
|
||||
Raises:
|
||||
HTTPException: If credentials are invalid
|
||||
"""
|
||||
user = authenticate_user(db, form_data.email, form_data.password)
|
||||
user, reason = authenticate_user(db, form_data.email, form_data.password)
|
||||
if not user:
|
||||
logger.warning(f"Login attempt with invalid credentials: {form_data.email}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid email or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
if reason == "user_not_found" or reason == "invalid_password":
|
||||
logger.warning(f"Login attempt with invalid credentials: {form_data.email}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid email or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
elif reason == "email_not_verified":
|
||||
logger.warning(f"Login attempt with unverified email: {form_data.email}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Email not verified",
|
||||
)
|
||||
elif reason == "inactive_user":
|
||||
logger.warning(f"Login attempt with inactive user: {form_data.email}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="User account is inactive",
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Login attempt failed for {form_data.email} (reason: {reason})"
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid email or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
access_token = create_access_token(user)
|
||||
logger.info(f"Login successful for user: {user.email}")
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: chat_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
@@ -55,8 +84,8 @@ async def websocket_chat(
|
||||
auth_data = await websocket.receive_json()
|
||||
logger.info(f"Received authentication data: {auth_data}")
|
||||
|
||||
if not auth_data.get("type") == "authorization" or not auth_data.get(
|
||||
"token"
|
||||
if not (
|
||||
auth_data.get("type") == "authorization" and auth_data.get("token")
|
||||
):
|
||||
logger.warning("Invalid authentication message")
|
||||
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
||||
@@ -159,7 +188,7 @@ async def chat(
|
||||
await verify_user_client(payload, db, agent.client_id)
|
||||
|
||||
try:
|
||||
final_response_text = await run_agent(
|
||||
final_response = await run_agent(
|
||||
request.agent_id,
|
||||
request.external_id,
|
||||
request.message,
|
||||
@@ -170,14 +199,15 @@ async def chat(
|
||||
)
|
||||
|
||||
return {
|
||||
"response": final_response_text,
|
||||
"response": final_response["final_response"],
|
||||
"message_history": final_response["message_history"],
|
||||
"status": "success",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
}
|
||||
|
||||
except AgentNotFoundError as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
|
||||
)
|
||||
) from e
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: client_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: mcp_server_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from src.config.database import get_db
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: session_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from src.config.database import get_db
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: tool_routes.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from src.config.database import get_db
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: database.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: redis.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
"""
|
||||
Redis configuration module.
|
||||
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: settings.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Optional, List
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: exceptions.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import HTTPException
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: jwt_middleware.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from fastapi import HTTPException, Depends, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
|
||||
29
src/main.py
29
src/main.py
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: main.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: models.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import os
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: a2a_types.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: agent_config.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Dict, Union, Any
|
||||
from pydantic import BaseModel, Field
|
||||
from uuid import UUID
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: audit.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: chat.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: schemas.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, validator, UUID4, ConfigDict
|
||||
from typing import Optional, Dict, Any, List
|
||||
from datetime import datetime
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: streaming.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Any, Literal
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: user.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: a2a_agent.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from google.adk.agents import BaseAgent
|
||||
from google.adk.agents.invocation_context import InvocationContext
|
||||
from google.adk.events import Event
|
||||
|
||||
@@ -1,8 +1,38 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: a2a_task_manager.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import logging
|
||||
import asyncio
|
||||
from collections.abc import AsyncIterable
|
||||
from typing import Dict, Optional
|
||||
from uuid import UUID
|
||||
import json
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -236,29 +266,31 @@ class A2ATaskManager:
|
||||
) -> JSONRPCResponse:
|
||||
"""Processes a task using the specified agent."""
|
||||
task_params = request.params
|
||||
query = self._extract_user_query(task_params)
|
||||
|
||||
try:
|
||||
# Process the query with the agent
|
||||
result = await self._run_agent(agent, query, task_params.sessionId)
|
||||
query = self._extract_user_query(task_params)
|
||||
result_obj = await self._run_agent(agent, query, task_params.sessionId)
|
||||
|
||||
# Create the response part
|
||||
text_part = {"type": "text", "text": result}
|
||||
parts = [text_part]
|
||||
agent_message = Message(role="agent", parts=parts)
|
||||
|
||||
# Determine the task state
|
||||
task_state = (
|
||||
TaskState.INPUT_REQUIRED
|
||||
if "MISSING_INFO:" in result
|
||||
else TaskState.COMPLETED
|
||||
all_messages = await self._extract_messages_from_history(
|
||||
result_obj.get("message_history", [])
|
||||
)
|
||||
|
||||
# Update the task in the store
|
||||
result = result_obj["final_response"]
|
||||
agent_message = self._create_result_message(result)
|
||||
|
||||
if not all_messages and result:
|
||||
all_messages.append(agent_message)
|
||||
|
||||
task_state = self._determine_task_state(result)
|
||||
artifact = Artifact(parts=agent_message.parts, index=0)
|
||||
|
||||
task = await self.update_store(
|
||||
task_params.id,
|
||||
TaskStatus(state=task_state, message=agent_message),
|
||||
[Artifact(parts=parts, index=0)],
|
||||
[artifact],
|
||||
)
|
||||
|
||||
await self._update_task_history(
|
||||
task_params.id, task_params.message, all_messages
|
||||
)
|
||||
|
||||
return SendTaskResponse(id=request.id, result=task)
|
||||
@@ -269,12 +301,73 @@ class A2ATaskManager:
|
||||
error=InternalError(message=f"Error processing task: {str(e)}"),
|
||||
)
|
||||
|
||||
async def _extract_messages_from_history(self, agent_history):
|
||||
"""Extracts messages from the agent history."""
|
||||
all_messages = []
|
||||
for message_event in agent_history:
|
||||
try:
|
||||
if (
|
||||
not isinstance(message_event, dict)
|
||||
or "content" not in message_event
|
||||
):
|
||||
continue
|
||||
|
||||
content = message_event.get("content", {})
|
||||
if not isinstance(content, dict):
|
||||
continue
|
||||
|
||||
role = content.get("role", "agent")
|
||||
if role not in ["user", "agent"]:
|
||||
role = "agent"
|
||||
|
||||
parts = content.get("parts", [])
|
||||
if not parts:
|
||||
continue
|
||||
|
||||
if valid_parts := self._validate_message_parts(parts):
|
||||
agent_message = Message(role=role, parts=valid_parts)
|
||||
all_messages.append(agent_message)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing message history: {e}")
|
||||
return all_messages
|
||||
|
||||
def _validate_message_parts(self, parts):
|
||||
"""Validates and formats message parts."""
|
||||
valid_parts = []
|
||||
for part in parts:
|
||||
if isinstance(part, dict):
|
||||
if "type" not in part and "text" in part:
|
||||
part["type"] = "text"
|
||||
valid_parts.append(part)
|
||||
elif "type" in part:
|
||||
valid_parts.append(part)
|
||||
return valid_parts
|
||||
|
||||
def _create_result_message(self, result):
|
||||
"""Creates a message from the result."""
|
||||
text_part = {"type": "text", "text": result}
|
||||
return Message(role="agent", parts=[text_part])
|
||||
|
||||
def _determine_task_state(self, result):
|
||||
"""Determines the task state based on the result."""
|
||||
return (
|
||||
TaskState.INPUT_REQUIRED
|
||||
if "MISSING_INFO:" in result
|
||||
else TaskState.COMPLETED
|
||||
)
|
||||
|
||||
async def _update_task_history(self, task_id, user_message, agent_messages):
|
||||
"""Updates the task history."""
|
||||
async with self.lock:
|
||||
if task_id in self.tasks:
|
||||
task = self.tasks[task_id]
|
||||
task.history = [user_message] + agent_messages
|
||||
|
||||
async def _stream_task_process(
|
||||
self, request: SendTaskStreamingRequest, agent: Agent
|
||||
) -> AsyncIterable[SendTaskStreamingResponse]:
|
||||
"""Processes a task in streaming mode using the specified agent."""
|
||||
task_params = request.params
|
||||
query = self._extract_user_query(task_params)
|
||||
query = self._extract_user_query(request.params)
|
||||
|
||||
try:
|
||||
# Send initial processing status
|
||||
@@ -286,14 +379,14 @@ class A2ATaskManager:
|
||||
|
||||
# Update the task with the processing message and inform the WORKING state
|
||||
await self.update_store(
|
||||
task_params.id,
|
||||
request.params.id,
|
||||
TaskStatus(state=TaskState.WORKING, message=processing_message),
|
||||
)
|
||||
|
||||
yield SendTaskStreamingResponse(
|
||||
id=request.id,
|
||||
result=TaskStatusUpdateEvent(
|
||||
id=task_params.id,
|
||||
id=request.params.id,
|
||||
status=TaskStatus(
|
||||
state=TaskState.WORKING,
|
||||
message=processing_message,
|
||||
@@ -302,11 +395,11 @@ class A2ATaskManager:
|
||||
),
|
||||
)
|
||||
|
||||
# Collect the chunks of the agent's response
|
||||
external_id = task_params.sessionId
|
||||
external_id = request.params.sessionId
|
||||
full_response = ""
|
||||
|
||||
# We use the same streaming function used in the WebSocket
|
||||
final_message = None
|
||||
|
||||
async for chunk in run_agent_stream(
|
||||
agent_id=str(agent.id),
|
||||
external_id=external_id,
|
||||
@@ -316,47 +409,78 @@ class A2ATaskManager:
|
||||
memory_service=memory_service,
|
||||
db=self.db,
|
||||
):
|
||||
# Send incremental progress updates
|
||||
update_text_part = {"type": "text", "text": chunk}
|
||||
update_message = Message(role="agent", parts=[update_text_part])
|
||||
try:
|
||||
chunk_data = json.loads(chunk)
|
||||
except Exception as e:
|
||||
logger.warning(f"Invalid chunk received: {chunk} - {e}")
|
||||
continue
|
||||
|
||||
# Update the task with each intermediate message
|
||||
await self.update_store(
|
||||
task_params.id,
|
||||
TaskStatus(state=TaskState.WORKING, message=update_message),
|
||||
)
|
||||
if (
|
||||
isinstance(chunk_data, dict)
|
||||
and "type" in chunk_data
|
||||
and chunk_data["type"]
|
||||
in [
|
||||
"history",
|
||||
"history_update",
|
||||
"history_complete",
|
||||
]
|
||||
):
|
||||
continue
|
||||
|
||||
yield SendTaskStreamingResponse(
|
||||
id=request.id,
|
||||
result=TaskStatusUpdateEvent(
|
||||
id=task_params.id,
|
||||
status=TaskStatus(
|
||||
state=TaskState.WORKING,
|
||||
message=update_message,
|
||||
),
|
||||
final=False,
|
||||
),
|
||||
)
|
||||
full_response += chunk
|
||||
if isinstance(chunk_data, dict):
|
||||
if "type" not in chunk_data and "text" in chunk_data:
|
||||
chunk_data["type"] = "text"
|
||||
|
||||
# Determine the task state
|
||||
if "type" in chunk_data:
|
||||
try:
|
||||
update_message = Message(role="agent", parts=[chunk_data])
|
||||
|
||||
await self.update_store(
|
||||
request.params.id,
|
||||
TaskStatus(
|
||||
state=TaskState.WORKING, message=update_message
|
||||
),
|
||||
update_history=False,
|
||||
)
|
||||
|
||||
yield SendTaskStreamingResponse(
|
||||
id=request.id,
|
||||
result=TaskStatusUpdateEvent(
|
||||
id=request.params.id,
|
||||
status=TaskStatus(
|
||||
state=TaskState.WORKING,
|
||||
message=update_message,
|
||||
),
|
||||
final=False,
|
||||
),
|
||||
)
|
||||
|
||||
if chunk_data.get("type") == "text":
|
||||
full_response += chunk_data.get("text", "")
|
||||
final_message = update_message
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error processing chunk: {e}, chunk: {chunk_data}"
|
||||
)
|
||||
|
||||
# Determine the final state of the task
|
||||
task_state = (
|
||||
TaskState.INPUT_REQUIRED
|
||||
if "MISSING_INFO:" in full_response
|
||||
else TaskState.COMPLETED
|
||||
)
|
||||
|
||||
# Create the final response part
|
||||
final_text_part = {"type": "text", "text": full_response}
|
||||
parts = [final_text_part]
|
||||
final_message = Message(role="agent", parts=parts)
|
||||
# Create the final response if we don't have one yet
|
||||
if not final_message:
|
||||
final_text_part = {"type": "text", "text": full_response}
|
||||
parts = [final_text_part]
|
||||
final_message = Message(role="agent", parts=parts)
|
||||
|
||||
# Create the final artifact from the final response
|
||||
final_artifact = Artifact(parts=parts, index=0)
|
||||
final_artifact = Artifact(parts=final_message.parts, index=0)
|
||||
|
||||
# Update the task in the store with the final response
|
||||
await self.update_store(
|
||||
task_params.id,
|
||||
task = await self.update_store(
|
||||
request.params.id,
|
||||
TaskStatus(state=task_state, message=final_message),
|
||||
[final_artifact],
|
||||
)
|
||||
@@ -365,7 +489,7 @@ class A2ATaskManager:
|
||||
yield SendTaskStreamingResponse(
|
||||
id=request.id,
|
||||
result=TaskArtifactUpdateEvent(
|
||||
id=task_params.id, artifact=final_artifact
|
||||
id=request.params.id, artifact=final_artifact
|
||||
),
|
||||
)
|
||||
|
||||
@@ -373,7 +497,7 @@ class A2ATaskManager:
|
||||
yield SendTaskStreamingResponse(
|
||||
id=request.id,
|
||||
result=TaskStatusUpdateEvent(
|
||||
id=task_params.id,
|
||||
id=request.params.id,
|
||||
status=TaskStatus(state=task_state),
|
||||
final=True,
|
||||
),
|
||||
@@ -390,6 +514,7 @@ class A2ATaskManager:
|
||||
task_id: str,
|
||||
status: TaskStatus,
|
||||
artifacts: Optional[list[Artifact]] = None,
|
||||
update_history: bool = True,
|
||||
) -> Task:
|
||||
"""Updates the status and artifacts of a task."""
|
||||
async with self.lock:
|
||||
@@ -399,8 +524,8 @@ class A2ATaskManager:
|
||||
task = self.tasks[task_id]
|
||||
task.status = status
|
||||
|
||||
# Add message to history if it exists
|
||||
if status.message is not None:
|
||||
# Add message to history if it exists and update_history is True
|
||||
if status.message is not None and update_history:
|
||||
if task.history is None:
|
||||
task.history = []
|
||||
task.history.append(status.message)
|
||||
@@ -423,27 +548,22 @@ class A2ATaskManager:
|
||||
|
||||
return part.text
|
||||
|
||||
async def _run_agent(self, agent: Agent, query: str, session_id: str) -> str:
|
||||
async def _run_agent(self, agent: Agent, query: str, session_id: str) -> dict:
|
||||
"""Executes the agent to process the user query."""
|
||||
try:
|
||||
# We use the session_id as external_id to maintain the conversation continuity
|
||||
external_id = session_id
|
||||
|
||||
# We call the same function used in the chat API
|
||||
final_response = await run_agent(
|
||||
return await run_agent(
|
||||
agent_id=str(agent.id),
|
||||
external_id=external_id,
|
||||
external_id=session_id,
|
||||
message=query,
|
||||
session_service=session_service,
|
||||
artifacts_service=artifacts_service,
|
||||
memory_service=memory_service,
|
||||
db=self.db,
|
||||
)
|
||||
|
||||
return final_response
|
||||
except Exception as e:
|
||||
logger.error(f"Error running agent: {e}")
|
||||
raise ValueError(f"Error running agent: {str(e)}")
|
||||
raise ValueError(f"Error running agent: {str(e)}") from e
|
||||
|
||||
def append_task_history(self, task: Task, history_length: int | None) -> Task:
|
||||
"""Returns a copy of the task with the history limited to the specified size."""
|
||||
@@ -451,11 +571,16 @@ class A2ATaskManager:
|
||||
new_task = task.model_copy()
|
||||
|
||||
# Limit the history if requested
|
||||
if history_length is not None:
|
||||
if history_length is not None and new_task.history:
|
||||
if history_length > 0:
|
||||
new_task.history = (
|
||||
new_task.history[-history_length:] if new_task.history else []
|
||||
)
|
||||
if len(new_task.history) > history_length:
|
||||
user_message = new_task.history[0]
|
||||
recent_messages = (
|
||||
new_task.history[-(history_length - 1) :]
|
||||
if history_length > 1
|
||||
else []
|
||||
)
|
||||
new_task.history = [user_message] + recent_messages
|
||||
else:
|
||||
new_task.history = []
|
||||
|
||||
@@ -511,112 +636,11 @@ class A2AService:
|
||||
if not agent:
|
||||
raise ValueError(f"Agent {agent_id} not found")
|
||||
|
||||
# Build the agent card based on the agent's information
|
||||
capabilities = AgentCapabilities(streaming=True)
|
||||
capabilities = AgentCapabilities(
|
||||
streaming=True, pushNotifications=False, stateTransitionHistory=True
|
||||
)
|
||||
|
||||
# List to store all skills
|
||||
skills = []
|
||||
|
||||
# Check if the agent has MCP servers configured
|
||||
if (
|
||||
agent.config
|
||||
and "mcp_servers" in agent.config
|
||||
and agent.config["mcp_servers"]
|
||||
):
|
||||
logger.info(
|
||||
f"Agent {agent_id} has {len(agent.config['mcp_servers'])} MCP servers configured"
|
||||
)
|
||||
|
||||
for mcp_config in agent.config["mcp_servers"]:
|
||||
# Get the MCP server
|
||||
mcp_server_id = mcp_config.get("id")
|
||||
if not mcp_server_id:
|
||||
logger.warning("MCP server configuration missing ID")
|
||||
continue
|
||||
|
||||
logger.info(f"Processing MCP server: {mcp_server_id}")
|
||||
mcp_server = get_mcp_server(self.db, mcp_server_id)
|
||||
if not mcp_server:
|
||||
logger.warning(f"MCP server {mcp_server_id} not found")
|
||||
continue
|
||||
|
||||
# Get the available tools in the MCP server
|
||||
mcp_tools = mcp_config.get("tools", [])
|
||||
logger.info(f"MCP server {mcp_server.name} has tools: {mcp_tools}")
|
||||
|
||||
# Add server tools as skills
|
||||
for tool_name in mcp_tools:
|
||||
logger.info(f"Processing tool: {tool_name}")
|
||||
|
||||
tool_info = None
|
||||
if hasattr(mcp_server, "tools") and isinstance(
|
||||
mcp_server.tools, list
|
||||
):
|
||||
for tool in mcp_server.tools:
|
||||
if isinstance(tool, dict) and tool.get("id") == tool_name:
|
||||
tool_info = tool
|
||||
logger.info(
|
||||
f"Found tool info for {tool_name}: {tool_info}"
|
||||
)
|
||||
break
|
||||
|
||||
if tool_info:
|
||||
# Use the information from the tool
|
||||
skill = AgentSkill(
|
||||
id=tool_info.get("id", f"{agent.id}_{tool_name}"),
|
||||
name=tool_info.get("name", tool_name),
|
||||
description=tool_info.get(
|
||||
"description", f"Tool: {tool_name}"
|
||||
),
|
||||
tags=tool_info.get(
|
||||
"tags", [mcp_server.name, "tool", tool_name]
|
||||
),
|
||||
examples=tool_info.get("examples", []),
|
||||
inputModes=tool_info.get("inputModes", ["text"]),
|
||||
outputModes=tool_info.get("outputModes", ["text"]),
|
||||
)
|
||||
else:
|
||||
# Default skill if tool info not found
|
||||
skill = AgentSkill(
|
||||
id=f"{agent.id}_{tool_name}",
|
||||
name=tool_name,
|
||||
description=f"Tool: {tool_name}",
|
||||
tags=[mcp_server.name, "tool", tool_name],
|
||||
examples=[],
|
||||
inputModes=["text"],
|
||||
outputModes=["text"],
|
||||
)
|
||||
|
||||
skills.append(skill)
|
||||
logger.info(f"Added skill for tool: {tool_name}")
|
||||
|
||||
# Check custom tools
|
||||
if (
|
||||
agent.config
|
||||
and "custom_tools" in agent.config
|
||||
and agent.config["custom_tools"]
|
||||
):
|
||||
custom_tools = agent.config["custom_tools"]
|
||||
|
||||
# Check HTTP tools
|
||||
if "http_tools" in custom_tools and custom_tools["http_tools"]:
|
||||
logger.info(f"Agent has {len(custom_tools['http_tools'])} HTTP tools")
|
||||
for http_tool in custom_tools["http_tools"]:
|
||||
skill = AgentSkill(
|
||||
id=f"{agent.id}_http_{http_tool['name']}",
|
||||
name=http_tool["name"],
|
||||
description=http_tool.get(
|
||||
"description", f"HTTP Tool: {http_tool['name']}"
|
||||
),
|
||||
tags=http_tool.get(
|
||||
"tags", ["http", "custom_tool", http_tool["method"]]
|
||||
),
|
||||
examples=http_tool.get("examples", []),
|
||||
inputModes=http_tool.get("inputModes", ["text"]),
|
||||
outputModes=http_tool.get("outputModes", ["text"]),
|
||||
)
|
||||
skills.append(skill)
|
||||
logger.info(f"Added skill for HTTP tool: {http_tool['name']}")
|
||||
skills = self._get_agent_skills(agent)
|
||||
|
||||
card = AgentCard(
|
||||
name=agent.name,
|
||||
@@ -639,3 +663,134 @@ class A2AService:
|
||||
|
||||
logger.info(f"Generated agent card with {len(skills)} skills")
|
||||
return card
|
||||
|
||||
def _get_agent_skills(self, agent: Agent) -> list[AgentSkill]:
|
||||
"""Extracts the skills of an agent based on its configuration."""
|
||||
skills = []
|
||||
|
||||
if self._has_mcp_servers(agent):
|
||||
skills.extend(self._get_mcp_server_skills(agent))
|
||||
|
||||
if self._has_custom_tools(agent):
|
||||
skills.extend(self._get_custom_tool_skills(agent))
|
||||
|
||||
return skills
|
||||
|
||||
def _has_mcp_servers(self, agent: Agent) -> bool:
|
||||
"""Checks if the agent has MCP servers configured."""
|
||||
return (
|
||||
agent.config
|
||||
and "mcp_servers" in agent.config
|
||||
and agent.config["mcp_servers"]
|
||||
)
|
||||
|
||||
def _has_custom_tools(self, agent: Agent) -> bool:
|
||||
"""Checks if the agent has custom tools configured."""
|
||||
return (
|
||||
agent.config
|
||||
and "custom_tools" in agent.config
|
||||
and agent.config["custom_tools"]
|
||||
)
|
||||
|
||||
def _get_mcp_server_skills(self, agent: Agent) -> list[AgentSkill]:
|
||||
"""Gets the skills of the MCP servers configured for the agent."""
|
||||
skills = []
|
||||
logger.info(
|
||||
f"Agent {agent.id} has {len(agent.config['mcp_servers'])} MCP servers configured"
|
||||
)
|
||||
|
||||
for mcp_config in agent.config["mcp_servers"]:
|
||||
mcp_server_id = mcp_config.get("id")
|
||||
if not mcp_server_id:
|
||||
logger.warning("MCP server configuration missing ID")
|
||||
continue
|
||||
|
||||
mcp_server = get_mcp_server(self.db, mcp_server_id)
|
||||
if not mcp_server:
|
||||
logger.warning(f"MCP server {mcp_server_id} not found")
|
||||
continue
|
||||
|
||||
skills.extend(self._extract_mcp_tool_skills(agent, mcp_server, mcp_config))
|
||||
|
||||
return skills
|
||||
|
||||
def _extract_mcp_tool_skills(
|
||||
self, agent: Agent, mcp_server, mcp_config
|
||||
) -> list[AgentSkill]:
|
||||
"""Extracts skills from MCP tools."""
|
||||
skills = []
|
||||
mcp_tools = mcp_config.get("tools", [])
|
||||
logger.info(f"MCP server {mcp_server.name} has tools: {mcp_tools}")
|
||||
|
||||
for tool_name in mcp_tools:
|
||||
tool_info = self._find_tool_info(mcp_server, tool_name)
|
||||
skill = self._create_tool_skill(
|
||||
agent, tool_name, tool_info, mcp_server.name
|
||||
)
|
||||
skills.append(skill)
|
||||
logger.info(f"Added skill for tool: {tool_name}")
|
||||
|
||||
return skills
|
||||
|
||||
def _find_tool_info(self, mcp_server, tool_name) -> dict:
|
||||
"""Finds information about a tool in an MCP server."""
|
||||
if not hasattr(mcp_server, "tools") or not isinstance(mcp_server.tools, list):
|
||||
return None
|
||||
|
||||
for tool in mcp_server.tools:
|
||||
if isinstance(tool, dict) and tool.get("id") == tool_name:
|
||||
logger.info(f"Found tool info for {tool_name}: {tool}")
|
||||
return tool
|
||||
|
||||
return None
|
||||
|
||||
def _create_tool_skill(
|
||||
self, agent: Agent, tool_name: str, tool_info: dict, server_name: str
|
||||
) -> AgentSkill:
|
||||
"""Creates an AgentSkill object based on the tool information."""
|
||||
if tool_info:
|
||||
return AgentSkill(
|
||||
id=tool_info.get("id", f"{agent.id}_{tool_name}"),
|
||||
name=tool_info.get("name", tool_name),
|
||||
description=tool_info.get("description", f"Tool: {tool_name}"),
|
||||
tags=tool_info.get("tags", [server_name, "tool", tool_name]),
|
||||
examples=tool_info.get("examples", []),
|
||||
inputModes=tool_info.get("inputModes", ["text"]),
|
||||
outputModes=tool_info.get("outputModes", ["text"]),
|
||||
)
|
||||
else:
|
||||
return AgentSkill(
|
||||
id=f"{agent.id}_{tool_name}",
|
||||
name=tool_name,
|
||||
description=f"Tool: {tool_name}",
|
||||
tags=[server_name, "tool", tool_name],
|
||||
examples=[],
|
||||
inputModes=["text"],
|
||||
outputModes=["text"],
|
||||
)
|
||||
|
||||
def _get_custom_tool_skills(self, agent: Agent) -> list[AgentSkill]:
|
||||
"""Gets the skills of the custom tools of the agent."""
|
||||
skills = []
|
||||
custom_tools = agent.config["custom_tools"]
|
||||
|
||||
if "http_tools" in custom_tools and custom_tools["http_tools"]:
|
||||
logger.info(f"Agent has {len(custom_tools['http_tools'])} HTTP tools")
|
||||
for http_tool in custom_tools["http_tools"]:
|
||||
skill = AgentSkill(
|
||||
id=f"{agent.id}_http_{http_tool['name']}",
|
||||
name=http_tool["name"],
|
||||
description=http_tool.get(
|
||||
"description", f"HTTP Tool: {http_tool['name']}"
|
||||
),
|
||||
tags=http_tool.get(
|
||||
"tags", ["http", "custom_tool", http_tool["method"]]
|
||||
),
|
||||
examples=http_tool.get("examples", []),
|
||||
inputModes=http_tool.get("inputModes", ["text"]),
|
||||
outputModes=http_tool.get("outputModes", ["text"]),
|
||||
)
|
||||
skills.append(skill)
|
||||
logger.info(f"Added skill for HTTP tool: {http_tool['name']}")
|
||||
|
||||
return skills
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: agent_builder.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Tuple
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
from google.adk.agents import SequentialAgent, ParallelAgent, LoopAgent, BaseAgent
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: agent_runner.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from google.adk.runners import Runner
|
||||
from google.genai.types import Content, Part
|
||||
from google.adk.sessions import DatabaseSessionService
|
||||
@@ -65,7 +94,7 @@ async def run_agent(
|
||||
artifact_service=artifacts_service,
|
||||
memory_service=memory_service,
|
||||
)
|
||||
adk_session_id = external_id + "_" + agent_id
|
||||
adk_session_id = f"{external_id}_{agent_id}"
|
||||
if session_id is None:
|
||||
session_id = adk_session_id
|
||||
|
||||
@@ -88,6 +117,8 @@ async def run_agent(
|
||||
logger.info("Starting agent execution")
|
||||
|
||||
final_response_text = "No final response captured."
|
||||
message_history = []
|
||||
|
||||
try:
|
||||
response_queue = asyncio.Queue()
|
||||
execution_completed = asyncio.Event()
|
||||
@@ -104,6 +135,11 @@ async def run_agent(
|
||||
all_responses = []
|
||||
|
||||
async for event in events_async:
|
||||
if event.content and event.content.parts:
|
||||
event_dict = event.dict()
|
||||
event_dict = convert_sets(event_dict)
|
||||
message_history.append(event_dict)
|
||||
|
||||
if (
|
||||
event.content
|
||||
and event.content.parts
|
||||
@@ -176,10 +212,13 @@ async def run_agent(
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing request: {str(e)}")
|
||||
raise e
|
||||
raise InternalServerError(str(e)) from e
|
||||
|
||||
logger.info("Agent execution completed successfully")
|
||||
return final_response_text
|
||||
return {
|
||||
"final_response": final_response_text,
|
||||
"message_history": message_history,
|
||||
}
|
||||
except AgentNotFoundError as e:
|
||||
logger.error(f"Error processing request: {str(e)}")
|
||||
raise e
|
||||
@@ -256,7 +295,7 @@ async def run_agent_stream(
|
||||
artifact_service=artifacts_service,
|
||||
memory_service=memory_service,
|
||||
)
|
||||
adk_session_id = external_id + "_" + agent_id
|
||||
adk_session_id = f"{external_id}_{agent_id}"
|
||||
if session_id is None:
|
||||
session_id = adk_session_id
|
||||
|
||||
@@ -286,9 +325,51 @@ async def run_agent_stream(
|
||||
)
|
||||
|
||||
async for event in events_async:
|
||||
event_dict = event.dict()
|
||||
event_dict = convert_sets(event_dict)
|
||||
yield json.dumps(event_dict)
|
||||
try:
|
||||
event_dict = event.dict()
|
||||
event_dict = convert_sets(event_dict)
|
||||
|
||||
if "content" in event_dict and event_dict["content"]:
|
||||
content = event_dict["content"]
|
||||
|
||||
if "role" not in content or content["role"] not in [
|
||||
"user",
|
||||
"agent",
|
||||
]:
|
||||
content["role"] = "agent"
|
||||
|
||||
if "parts" in content and content["parts"]:
|
||||
valid_parts = []
|
||||
for part in content["parts"]:
|
||||
if isinstance(part, dict):
|
||||
if "type" not in part and "text" in part:
|
||||
part["type"] = "text"
|
||||
valid_parts.append(part)
|
||||
elif "type" in part:
|
||||
valid_parts.append(part)
|
||||
|
||||
if valid_parts:
|
||||
content["parts"] = valid_parts
|
||||
else:
|
||||
content["parts"] = [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Content without valid format",
|
||||
}
|
||||
]
|
||||
else:
|
||||
content["parts"] = [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Content without parts",
|
||||
}
|
||||
]
|
||||
|
||||
# Send the individual event
|
||||
yield json.dumps(event_dict)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing event: {e}")
|
||||
continue
|
||||
|
||||
completed_session = session_service.get_session(
|
||||
app_name=agent_id,
|
||||
@@ -299,7 +380,7 @@ async def run_agent_stream(
|
||||
memory_service.add_session_to_memory(completed_session)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing request: {str(e)}")
|
||||
raise e
|
||||
raise InternalServerError(str(e)) from e
|
||||
finally:
|
||||
# Clean up MCP connection
|
||||
if exit_stack:
|
||||
@@ -312,7 +393,7 @@ async def run_agent_stream(
|
||||
logger.info("Agent streaming execution completed successfully")
|
||||
except AgentNotFoundError as e:
|
||||
logger.error(f"Error processing request: {str(e)}")
|
||||
raise e
|
||||
raise InternalServerError(str(e)) from e
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Internal error processing request: {str(e)}", exc_info=True
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: agent_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: apikey_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from src.models.models import ApiKey
|
||||
from src.utils.crypto import encrypt_api_key, decrypt_api_key
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: audit_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from src.models.models import AuditLog
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: auth_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from src.models.models import User
|
||||
from src.schemas.user import TokenData
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: client_service.p │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: custom_tools.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from google.adk.tools import FunctionTool
|
||||
import requests
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: email_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import sendgrid
|
||||
from sendgrid.helpers.mail import Mail, Email, To, Content
|
||||
from src.config.settings import settings
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||
@@ -51,12 +79,12 @@ def send_verification_email(email: str, token: str) -> bool:
|
||||
bool: True if the email was sent successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
||||
from_email = Email(settings.EMAIL_FROM)
|
||||
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||
to_email = To(email)
|
||||
subject = "Email Verification - Evo AI"
|
||||
|
||||
verification_link = f"{settings.APP_URL}/api/v1/auth/verify-email/{token}"
|
||||
verification_link = f"{os.getenv('APP_URL')}/security/verify-email?code={token}"
|
||||
|
||||
html_content = _render_template(
|
||||
"verification_email",
|
||||
@@ -100,12 +128,12 @@ def send_password_reset_email(email: str, token: str) -> bool:
|
||||
bool: True if the email was sent successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
||||
from_email = Email(settings.EMAIL_FROM)
|
||||
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||
to_email = To(email)
|
||||
subject = "Password Reset - Evo AI"
|
||||
|
||||
reset_link = f"{settings.APP_URL}/reset-password?token={token}"
|
||||
reset_link = f"{os.getenv('APP_URL')}/security/reset-password?token={token}"
|
||||
|
||||
html_content = _render_template(
|
||||
"password_reset",
|
||||
@@ -149,12 +177,12 @@ def send_welcome_email(email: str, user_name: str = None) -> bool:
|
||||
bool: True if the email was sent successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
||||
from_email = Email(settings.EMAIL_FROM)
|
||||
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||
to_email = To(email)
|
||||
subject = "Welcome to Evo AI"
|
||||
|
||||
dashboard_link = f"{settings.APP_URL}/dashboard"
|
||||
dashboard_link = f"{os.getenv('APP_URL')}/dashboard"
|
||||
|
||||
html_content = _render_template(
|
||||
"welcome_email",
|
||||
@@ -200,12 +228,14 @@ def send_account_locked_email(
|
||||
bool: True if the email was sent successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
||||
from_email = Email(settings.EMAIL_FROM)
|
||||
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||
to_email = To(email)
|
||||
subject = "Security Alert - Account Locked"
|
||||
|
||||
reset_link = f"{settings.APP_URL}/reset-password?token={reset_token}"
|
||||
reset_link = (
|
||||
f"{os.getenv('APP_URL')}/security/reset-password?token={reset_token}"
|
||||
)
|
||||
|
||||
html_content = _render_template(
|
||||
"account_locked",
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: run_seeders.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: mcp_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from google.adk.tools.mcp_tool.mcp_toolset import (
|
||||
MCPToolset,
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: service_providers.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from src.config.settings import settings
|
||||
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
|
||||
from google.adk.sessions import DatabaseSessionService
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: session_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from google.adk.sessions import DatabaseSessionService
|
||||
from sqlalchemy.orm import Session
|
||||
from src.models.models import Session as SessionModel
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: tool_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: user_service.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from src.models.models import User, Client
|
||||
@@ -7,7 +36,7 @@ from src.services.email_service import (
|
||||
send_verification_email,
|
||||
send_password_reset_email,
|
||||
)
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import uuid
|
||||
import logging
|
||||
from typing import Optional, Tuple
|
||||
@@ -295,7 +324,11 @@ def reset_password(db: Session, token: str, new_password: str) -> Tuple[bool, st
|
||||
return False, "Invalid password reset token"
|
||||
|
||||
# Check if the token has expired
|
||||
if user.password_reset_expiry < datetime.utcnow():
|
||||
now = datetime.now(timezone.utc)
|
||||
expiry = user.password_reset_expiry
|
||||
if expiry is not None and expiry.tzinfo is None:
|
||||
expiry = expiry.replace(tzinfo=timezone.utc)
|
||||
if expiry is None or expiry < now:
|
||||
logger.warning(
|
||||
f"Attempt to reset password with expired token for user: {user.email}"
|
||||
)
|
||||
@@ -341,7 +374,9 @@ def get_user_by_email(db: Session, email: str) -> Optional[User]:
|
||||
return None
|
||||
|
||||
|
||||
def authenticate_user(db: Session, email: str, password: str) -> Optional[User]:
|
||||
def authenticate_user(
|
||||
db: Session, email: str, password: str
|
||||
) -> Tuple[Optional[User], str]:
|
||||
"""
|
||||
Authenticates a user with email and password
|
||||
|
||||
@@ -351,16 +386,18 @@ def authenticate_user(db: Session, email: str, password: str) -> Optional[User]:
|
||||
password: User password
|
||||
|
||||
Returns:
|
||||
Optional[User]: Authenticated user or None
|
||||
Tuple[Optional[User], str]: Authenticated user and reason (or None and reason)
|
||||
"""
|
||||
user = get_user_by_email(db, email)
|
||||
if not user:
|
||||
return None
|
||||
return None, "user_not_found"
|
||||
if not verify_password(password, user.password_hash):
|
||||
return None
|
||||
return None, "invalid_password"
|
||||
if not user.email_verified:
|
||||
return None, "email_not_verified"
|
||||
if not user.is_active:
|
||||
return None
|
||||
return user
|
||||
return None, "inactive_user"
|
||||
return user, "success"
|
||||
|
||||
|
||||
def get_admin_users(db: Session, skip: int = 0, limit: int = 100):
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: workflow_agent.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from google.adk.agents import BaseAgent
|
||||
from google.adk.agents.invocation_context import InvocationContext
|
||||
@@ -87,7 +116,7 @@ class WorkflowAgent(BaseAgent):
|
||||
if not content:
|
||||
content = [
|
||||
Event(
|
||||
author="agent",
|
||||
author="workflow_agent",
|
||||
content=Content(parts=[Part(text="Content not found")]),
|
||||
)
|
||||
]
|
||||
@@ -139,7 +168,7 @@ class WorkflowAgent(BaseAgent):
|
||||
yield {
|
||||
"content": [
|
||||
Event(
|
||||
author="agent",
|
||||
author="workflow_agent",
|
||||
content=Content(parts=[Part(text="Agent not found")]),
|
||||
)
|
||||
],
|
||||
@@ -162,7 +191,7 @@ class WorkflowAgent(BaseAgent):
|
||||
conversation_history.append(event)
|
||||
new_content.append(event)
|
||||
|
||||
print(f"New content: {str(new_content)}")
|
||||
print(f"New content: {new_content}")
|
||||
|
||||
node_outputs = state.get("node_outputs", {})
|
||||
node_outputs[node_id] = {
|
||||
@@ -252,7 +281,7 @@ class WorkflowAgent(BaseAgent):
|
||||
|
||||
condition_content = [
|
||||
Event(
|
||||
author="agent",
|
||||
author="workflow_agent",
|
||||
content=Content(parts=[Part(text="Cycle limit reached")]),
|
||||
)
|
||||
]
|
||||
@@ -283,7 +312,7 @@ class WorkflowAgent(BaseAgent):
|
||||
|
||||
condition_content = [
|
||||
Event(
|
||||
author="agent",
|
||||
author=label,
|
||||
content=Content(
|
||||
parts=[
|
||||
Part(
|
||||
@@ -317,8 +346,10 @@ class WorkflowAgent(BaseAgent):
|
||||
session_id = state.get("session_id", "")
|
||||
conversation_history = state.get("conversation_history", [])
|
||||
|
||||
label = node_data.get("label", "message_node")
|
||||
|
||||
new_event = Event(
|
||||
author="agent",
|
||||
author=label,
|
||||
content=Content(parts=[Part(text=message_content)]),
|
||||
)
|
||||
content = content + [new_event]
|
||||
@@ -351,139 +382,160 @@ class WorkflowAgent(BaseAgent):
|
||||
condition_data = condition.get("data", {})
|
||||
|
||||
if condition_type == "previous-output":
|
||||
field = condition_data.get("field")
|
||||
operator = condition_data.get("operator")
|
||||
expected_value = condition_data.get("value")
|
||||
field, operator, expected_value, actual_value = (
|
||||
self._extract_condition_values(condition_data, state)
|
||||
)
|
||||
|
||||
actual_value = state.get(field, "")
|
||||
|
||||
# Special treatment for when content is a list of Events
|
||||
if field == "content" and isinstance(actual_value, list) and actual_value:
|
||||
# Extract text from each event for comparison
|
||||
extracted_texts = []
|
||||
for event in actual_value:
|
||||
if hasattr(event, "content") and hasattr(event.content, "parts"):
|
||||
for part in event.content.parts:
|
||||
if hasattr(part, "text") and part.text:
|
||||
extracted_texts.append(part.text)
|
||||
actual_value = self._extract_text_from_events(actual_value)
|
||||
|
||||
if extracted_texts:
|
||||
actual_value = " ".join(extracted_texts)
|
||||
print(f" Extracted text from events: '{actual_value[:100]}...'")
|
||||
result = self._process_condition(operator, actual_value, expected_value)
|
||||
|
||||
# Convert values to string for easier comparisons
|
||||
if actual_value is not None:
|
||||
actual_str = str(actual_value)
|
||||
else:
|
||||
actual_str = ""
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
|
||||
if expected_value is not None:
|
||||
expected_str = str(expected_value)
|
||||
else:
|
||||
expected_str = ""
|
||||
return False
|
||||
|
||||
# Checks for definition
|
||||
if operator == "is_defined":
|
||||
result = actual_value is not None and actual_value != ""
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
elif operator == "is_not_defined":
|
||||
result = actual_value is None or actual_value == ""
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
def _process_condition(self, operator, actual_value, expected_value):
|
||||
"""Converts values to strings and processes the condition using the appropriate operator."""
|
||||
actual_str = str(actual_value) if actual_value is not None else ""
|
||||
expected_str = str(expected_value) if expected_value is not None else ""
|
||||
|
||||
# Checks for equality
|
||||
elif operator == "equals":
|
||||
result = actual_str == expected_str
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
elif operator == "not_equals":
|
||||
result = actual_str != expected_str
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
return self._process_operator(operator, actual_value, actual_str, expected_str)
|
||||
|
||||
# Checks for content
|
||||
elif operator == "contains":
|
||||
# Convert both to lowercase for case-insensitive comparison
|
||||
expected_lower = expected_str.lower()
|
||||
actual_lower = actual_str.lower()
|
||||
print(
|
||||
f" Comparison 'contains' without case distinction: '{expected_lower}' in '{actual_lower[:100]}...'"
|
||||
def _extract_condition_values(self, condition_data, state):
|
||||
"""Extracts field, operator, expected value and actual value from condition data."""
|
||||
field = condition_data.get("field")
|
||||
operator = condition_data.get("operator")
|
||||
expected_value = condition_data.get("value")
|
||||
actual_value = state.get(field, "")
|
||||
|
||||
return field, operator, expected_value, actual_value
|
||||
|
||||
def _extract_text_from_events(self, events):
|
||||
"""Extracts text content from a list of events for comparison."""
|
||||
extracted_texts = []
|
||||
for event in events:
|
||||
if hasattr(event, "content") and hasattr(event.content, "parts"):
|
||||
extracted_texts.extend(
|
||||
[
|
||||
part.text
|
||||
for part in event.content.parts
|
||||
if hasattr(part, "text") and part.text
|
||||
]
|
||||
)
|
||||
result = expected_lower in actual_lower
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
elif operator == "not_contains":
|
||||
expected_lower = expected_str.lower()
|
||||
actual_lower = actual_str.lower()
|
||||
print(
|
||||
f" Comparison 'not_contains' without case distinction: '{expected_lower}' in '{actual_lower[:100]}...'"
|
||||
)
|
||||
result = expected_lower not in actual_lower
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
|
||||
# Checks for start and end
|
||||
elif operator == "starts_with":
|
||||
result = actual_str.lower().startswith(expected_str.lower())
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
elif operator == "ends_with":
|
||||
result = actual_str.lower().endswith(expected_str.lower())
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
if extracted_texts:
|
||||
joined_text = " ".join(extracted_texts)
|
||||
print(f" Extracted text from events: '{joined_text[:100]}...'")
|
||||
return joined_text
|
||||
|
||||
# Numeric checks (attempting to convert to number)
|
||||
elif operator in [
|
||||
"greater_than",
|
||||
"greater_than_or_equal",
|
||||
"less_than",
|
||||
"less_than_or_equal",
|
||||
]:
|
||||
try:
|
||||
actual_num = float(actual_str) if actual_str else 0
|
||||
expected_num = float(expected_str) if expected_str else 0
|
||||
return ""
|
||||
|
||||
if operator == "greater_than":
|
||||
result = actual_num > expected_num
|
||||
elif operator == "greater_than_or_equal":
|
||||
result = actual_num >= expected_num
|
||||
elif operator == "less_than":
|
||||
result = actual_num < expected_num
|
||||
elif operator == "less_than_or_equal":
|
||||
result = actual_num <= expected_num
|
||||
print(f" Numeric check '{operator}': {result}")
|
||||
return result
|
||||
except (ValueError, TypeError):
|
||||
# If it's not possible to convert to number, return false
|
||||
print(
|
||||
f" Error converting values for numeric comparison: '{actual_str[:100]}...' and '{expected_str}'"
|
||||
)
|
||||
return False
|
||||
def _process_operator(self, operator, actual_value, actual_str, expected_str):
|
||||
"""Process the operator and return the result of the comparison."""
|
||||
# Definition checks
|
||||
if operator in ["is_defined", "is_not_defined"]:
|
||||
return self._check_definition(operator, actual_value)
|
||||
|
||||
# Checks with regular expressions
|
||||
elif operator == "matches":
|
||||
import re
|
||||
# Equality checks
|
||||
elif operator in ["equals", "not_equals"]:
|
||||
return self._check_equality(operator, actual_str, expected_str)
|
||||
|
||||
try:
|
||||
pattern = re.compile(expected_str, re.IGNORECASE)
|
||||
result = bool(pattern.search(actual_str))
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
except re.error:
|
||||
print(f" Error in regular expression: '{expected_str}'")
|
||||
return False
|
||||
elif operator == "not_matches":
|
||||
import re
|
||||
# Content checks
|
||||
elif operator in ["contains", "not_contains"]:
|
||||
return self._case_insensitive_comparison(expected_str, actual_str, operator)
|
||||
|
||||
try:
|
||||
pattern = re.compile(expected_str, re.IGNORECASE)
|
||||
result = not bool(pattern.search(actual_str))
|
||||
print(f" Check '{operator}': {result}")
|
||||
return result
|
||||
except re.error:
|
||||
print(f" Error in regular expression: '{expected_str}'")
|
||||
return True # If the regex is invalid, we consider that there was no match
|
||||
# String pattern checks
|
||||
elif operator in ["starts_with", "ends_with"]:
|
||||
return self._check_string_pattern(operator, actual_str, expected_str)
|
||||
|
||||
# Numeric checks
|
||||
elif operator in [
|
||||
"greater_than",
|
||||
"greater_than_or_equal",
|
||||
"less_than",
|
||||
"less_than_or_equal",
|
||||
]:
|
||||
return self._check_numeric(operator, actual_str, expected_str)
|
||||
|
||||
# Regex checks
|
||||
elif operator in ["matches", "not_matches"]:
|
||||
return self._check_regex(operator, actual_str, expected_str)
|
||||
|
||||
return False
|
||||
|
||||
def _check_definition(self, operator, actual_value):
|
||||
"""Check if a value is defined or not."""
|
||||
if operator == "is_defined":
|
||||
return actual_value is not None and actual_value != ""
|
||||
else: # is_not_defined
|
||||
return actual_value is None or actual_value == ""
|
||||
|
||||
def _check_equality(self, operator, actual_str, expected_str):
|
||||
"""Check if two strings are equal or not."""
|
||||
return (
|
||||
(actual_str == expected_str)
|
||||
if operator == "equals"
|
||||
else (actual_str != expected_str)
|
||||
)
|
||||
|
||||
def _check_string_pattern(self, operator, actual_str, expected_str):
|
||||
"""Check if a string starts or ends with another string."""
|
||||
if operator == "starts_with":
|
||||
return actual_str.lower().startswith(expected_str.lower())
|
||||
else: # ends_with
|
||||
return actual_str.lower().endswith(expected_str.lower())
|
||||
|
||||
def _check_numeric(self, operator, actual_str, expected_str):
|
||||
"""Compare numeric values."""
|
||||
try:
|
||||
actual_num = float(actual_str) if actual_str else 0
|
||||
expected_num = float(expected_str) if expected_str else 0
|
||||
|
||||
if operator == "greater_than":
|
||||
return actual_num > expected_num
|
||||
elif operator == "greater_than_or_equal":
|
||||
return actual_num >= expected_num
|
||||
elif operator == "less_than":
|
||||
return actual_num < expected_num
|
||||
else: # less_than_or_equal
|
||||
return actual_num <= expected_num
|
||||
except (ValueError, TypeError):
|
||||
print(
|
||||
f" Error converting values for numeric comparison: '{actual_str[:100]}...' and '{expected_str}'"
|
||||
)
|
||||
return False
|
||||
|
||||
def _check_regex(self, operator, actual_str, expected_str):
|
||||
"""Check if a string matches a regex pattern."""
|
||||
import re
|
||||
|
||||
try:
|
||||
pattern = re.compile(expected_str, re.IGNORECASE)
|
||||
if operator == "matches":
|
||||
return bool(pattern.search(actual_str))
|
||||
else: # not_matches
|
||||
return not bool(pattern.search(actual_str))
|
||||
except re.error:
|
||||
print(f" Error in regular expression: '{expected_str}'")
|
||||
return (
|
||||
operator == "not_matches"
|
||||
) # Return True for not_matches, False for matches
|
||||
|
||||
def _case_insensitive_comparison(self, expected_str, actual_str, operator):
|
||||
"""Performs case-insensitive string comparison based on the specified operator."""
|
||||
expected_lower = expected_str.lower()
|
||||
actual_lower = actual_str.lower()
|
||||
|
||||
print(
|
||||
f" Comparison '{operator}' without case distinction: '{expected_lower}' in '{actual_lower[:100]}...'"
|
||||
)
|
||||
|
||||
if operator == "contains":
|
||||
return expected_lower in actual_lower
|
||||
elif operator == "not_contains":
|
||||
return expected_lower not in actual_lower
|
||||
|
||||
return False
|
||||
|
||||
@@ -529,38 +581,15 @@ class WorkflowAgent(BaseAgent):
|
||||
conditions = condition_nodes[node_id]
|
||||
any_condition_met = False
|
||||
|
||||
for condition in conditions:
|
||||
condition_id = condition.get("id")
|
||||
|
||||
# Get latest event for evaluation, ignoring condition node informational events
|
||||
content = state.get("content", [])
|
||||
latest_event = None
|
||||
for event in reversed(content):
|
||||
# Skip events generated by condition nodes
|
||||
if (
|
||||
event.author != "agent"
|
||||
or not hasattr(event.content, "parts")
|
||||
or not event.content.parts
|
||||
):
|
||||
latest_event = event
|
||||
break
|
||||
|
||||
evaluation_state = state.copy()
|
||||
if latest_event:
|
||||
evaluation_state["content"] = [latest_event]
|
||||
|
||||
# Check if the condition is met
|
||||
is_condition_met = self._evaluate_condition(
|
||||
condition, evaluation_state
|
||||
)
|
||||
|
||||
if is_condition_met:
|
||||
node_outputs = state.get("node_outputs", {})
|
||||
if node_id in node_outputs:
|
||||
conditions_met = node_outputs[node_id].get("conditions_met", [])
|
||||
if conditions_met:
|
||||
any_condition_met = True
|
||||
condition_id = conditions_met[0]
|
||||
print(
|
||||
f"Condition {condition_id} met. Moving to the next node."
|
||||
f"Using stored condition evaluation result: Condition {condition_id} met."
|
||||
)
|
||||
|
||||
# Find the connection that uses this condition_id as a handle
|
||||
if (
|
||||
node_id in edges_map
|
||||
and condition_id in edges_map[node_id]
|
||||
@@ -568,8 +597,49 @@ class WorkflowAgent(BaseAgent):
|
||||
return edges_map[node_id][condition_id]
|
||||
else:
|
||||
print(
|
||||
f"Condition {condition_id} not met. Continuing evaluation or using default path."
|
||||
"Using stored condition evaluation result: No conditions met."
|
||||
)
|
||||
else:
|
||||
for condition in conditions:
|
||||
condition_id = condition.get("id")
|
||||
|
||||
# Get latest event for evaluation, ignoring condition node informational events
|
||||
content = state.get("content", [])
|
||||
|
||||
# Filter out events generated by condition nodes or informational messages
|
||||
filtered_content = []
|
||||
for event in content:
|
||||
# Ignore events from condition nodes or that contain evaluation results
|
||||
if not hasattr(event, "author") or not (
|
||||
event.author.startswith("Condition")
|
||||
or "Condition evaluated:" in str(event)
|
||||
):
|
||||
filtered_content.append(event)
|
||||
|
||||
evaluation_state = state.copy()
|
||||
evaluation_state["content"] = filtered_content
|
||||
|
||||
# Check if the condition is met
|
||||
is_condition_met = self._evaluate_condition(
|
||||
condition, evaluation_state
|
||||
)
|
||||
|
||||
if is_condition_met:
|
||||
any_condition_met = True
|
||||
print(
|
||||
f"Condition {condition_id} met. Moving to the next node."
|
||||
)
|
||||
|
||||
# Find the connection that uses this condition_id as a handle
|
||||
if (
|
||||
node_id in edges_map
|
||||
and condition_id in edges_map[node_id]
|
||||
):
|
||||
return edges_map[node_id][condition_id]
|
||||
else:
|
||||
print(
|
||||
f"Condition {condition_id} not met. Continuing evaluation or using default path."
|
||||
)
|
||||
|
||||
# If no condition is met, use the bottom-handle if available
|
||||
if not any_condition_met:
|
||||
@@ -706,91 +776,96 @@ class WorkflowAgent(BaseAgent):
|
||||
async def _run_async_impl(
|
||||
self, ctx: InvocationContext
|
||||
) -> AsyncGenerator[Event, None]:
|
||||
"""
|
||||
Implementation of the workflow agent.
|
||||
|
||||
This method follows the pattern of custom agent implementation,
|
||||
executing the defined workflow and returning the results.
|
||||
"""
|
||||
|
||||
"""Implementation of the workflow agent executing the defined workflow and returning results."""
|
||||
try:
|
||||
# 1. Extract the user message from the context
|
||||
user_message = None
|
||||
|
||||
# Search for the user message in the session events
|
||||
if ctx.session and hasattr(ctx.session, "events") and ctx.session.events:
|
||||
for event in reversed(ctx.session.events):
|
||||
if event.author == "user" and event.content and event.content.parts:
|
||||
user_message = event.content.parts[0].text
|
||||
print("Message found in session events")
|
||||
break
|
||||
|
||||
# Check in the session state if the message was not found in the events
|
||||
if not user_message and ctx.session and ctx.session.state:
|
||||
if "user_message" in ctx.session.state:
|
||||
user_message = ctx.session.state["user_message"]
|
||||
elif "message" in ctx.session.state:
|
||||
user_message = ctx.session.state["message"]
|
||||
|
||||
# 2. Use the session ID as a stable identifier
|
||||
session_id = (
|
||||
str(ctx.session.id)
|
||||
if ctx.session and hasattr(ctx.session, "id")
|
||||
else str(uuid.uuid4())
|
||||
)
|
||||
|
||||
# 3. Create the workflow graph from the provided JSON
|
||||
user_message = await self._extract_user_message(ctx)
|
||||
session_id = self._get_session_id(ctx)
|
||||
graph = await self._create_graph(ctx, self.flow_json)
|
||||
|
||||
# 4. Prepare the initial state
|
||||
user_event = Event(
|
||||
author="user",
|
||||
content=Content(parts=[Part(text=user_message)]),
|
||||
initial_state = await self._prepare_initial_state(
|
||||
ctx, user_message, session_id
|
||||
)
|
||||
|
||||
# If the conversation history is empty, add the user message
|
||||
conversation_history = ctx.session.events or []
|
||||
if not conversation_history or (len(conversation_history) == 0):
|
||||
conversation_history = [user_event]
|
||||
|
||||
initial_state = State(
|
||||
content=[user_event],
|
||||
status="started",
|
||||
session_id=session_id,
|
||||
cycle_count=0,
|
||||
node_outputs={},
|
||||
conversation_history=conversation_history,
|
||||
)
|
||||
|
||||
# 5. Execute the graph
|
||||
print("\n🚀 Starting workflow execution:")
|
||||
print(f"Initial content: {user_message[:100]}...")
|
||||
|
||||
sent_events = 0 # Count of events already sent
|
||||
|
||||
async for state in graph.astream(initial_state, {"recursion_limit": 20}):
|
||||
# The state can be a dict with the node name as a key
|
||||
for node_state in state.values():
|
||||
content = node_state.get("content", [])
|
||||
# Only send new events
|
||||
for event in content[sent_events:]:
|
||||
if event.author != "user":
|
||||
yield event
|
||||
sent_events = len(content)
|
||||
|
||||
# Execute sub-agents
|
||||
for sub_agent in self.sub_agents:
|
||||
async for event in sub_agent.run_async(ctx):
|
||||
yield event
|
||||
# Iterar sobre o AsyncGenerator em vez de usar await
|
||||
async for event in self._execute_workflow(ctx, graph, initial_state):
|
||||
yield event
|
||||
|
||||
except Exception as e:
|
||||
# Handle any uncaught errors
|
||||
error_msg = f"Error executing the workflow agent: {str(e)}"
|
||||
print(error_msg)
|
||||
yield Event(
|
||||
author=self.name,
|
||||
content=Content(
|
||||
role="agent",
|
||||
parts=[Part(text=error_msg)],
|
||||
),
|
||||
)
|
||||
yield await self._handle_workflow_error(e)
|
||||
|
||||
async def _extract_user_message(self, ctx: InvocationContext) -> str:
|
||||
"""Extracts the user message from context session events or state."""
|
||||
# Try to find message in session events
|
||||
if ctx.session and hasattr(ctx.session, "events") and ctx.session.events:
|
||||
for event in reversed(ctx.session.events):
|
||||
if event.author == "user" and event.content and event.content.parts:
|
||||
print("Message found in session events")
|
||||
return event.content.parts[0].text
|
||||
|
||||
# Try to find message in session state
|
||||
if ctx.session and ctx.session.state:
|
||||
if "user_message" in ctx.session.state:
|
||||
return ctx.session.state["user_message"]
|
||||
elif "message" in ctx.session.state:
|
||||
return ctx.session.state["message"]
|
||||
|
||||
return ""
|
||||
|
||||
def _get_session_id(self, ctx: InvocationContext) -> str:
|
||||
"""Gets or generates a session ID."""
|
||||
if ctx.session and hasattr(ctx.session, "id"):
|
||||
return str(ctx.session.id)
|
||||
return str(uuid.uuid4())
|
||||
|
||||
async def _prepare_initial_state(
|
||||
self, ctx: InvocationContext, user_message: str, session_id: str
|
||||
) -> State:
|
||||
"""Prepares the initial state for workflow execution."""
|
||||
user_event = Event(
|
||||
author="user",
|
||||
content=Content(parts=[Part(text=user_message)]),
|
||||
)
|
||||
|
||||
conversation_history = ctx.session.events or [user_event]
|
||||
|
||||
return State(
|
||||
content=[user_event],
|
||||
status="started",
|
||||
session_id=session_id,
|
||||
cycle_count=0,
|
||||
node_outputs={},
|
||||
conversation_history=conversation_history,
|
||||
)
|
||||
|
||||
async def _execute_workflow(
|
||||
self, ctx: InvocationContext, graph: StateGraph, initial_state: State
|
||||
) -> AsyncGenerator[Event, None]:
|
||||
"""Executes the workflow graph and yields events."""
|
||||
sent_events = 0
|
||||
|
||||
async for state in graph.astream(initial_state, {"recursion_limit": 100}):
|
||||
for node_state in state.values():
|
||||
content = node_state.get("content", [])
|
||||
for event in content[sent_events:]:
|
||||
if event.author != "user":
|
||||
yield event
|
||||
sent_events = len(content)
|
||||
|
||||
# Execute sub-agents if any
|
||||
for sub_agent in self.sub_agents:
|
||||
async for event in sub_agent.run_async(ctx):
|
||||
yield event
|
||||
|
||||
async def _handle_workflow_error(self, error: Exception) -> Event:
|
||||
"""Creates an error event for workflow execution errors."""
|
||||
error_msg = f"Error executing the workflow agent: {str(error)}"
|
||||
print(error_msg)
|
||||
return Event(
|
||||
author=self.name,
|
||||
content=Content(
|
||||
role="agent",
|
||||
parts=[Part(text=error_msg)],
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,83 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% block title %}Evo AI{% endblock %}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f7f7f7;
|
||||
body {
|
||||
font-family: "Segoe UI", Arial, sans-serif;
|
||||
background-color: #f7f7f7;
|
||||
color: #222;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 480px;
|
||||
margin: 32px auto;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: #f7f7f7;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
padding: 32px 0 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.header h1 {
|
||||
color: #155a2c;
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.content {
|
||||
padding: 32px 24px 24px 24px;
|
||||
}
|
||||
.button {
|
||||
background: linear-gradient(90deg, #155a2c 0%, #1f7a3dff 100%);
|
||||
color: #fff !important;
|
||||
padding: 14px 0;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
margin: 32px 0 0 0;
|
||||
transition: filter 0.2s;
|
||||
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.08);
|
||||
}
|
||||
.button:hover {
|
||||
filter: brightness(1.08);
|
||||
}
|
||||
.footer {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
background: #f7f7f7;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
padding: 20px 0 10px 0;
|
||||
}
|
||||
.link {
|
||||
color: #155a2c;
|
||||
text-decoration: underline;
|
||||
word-break: break-all;
|
||||
}
|
||||
.warning {
|
||||
color: #b91c1c;
|
||||
background: #fee2e2;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
max-width: 98vw;
|
||||
margin: 8px;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.header {
|
||||
background-color: #4A90E2;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
border-radius: 6px 6px 0 0;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
.button {
|
||||
background-color: #4A90E2;
|
||||
color: white !important;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #3a7bc8;
|
||||
}
|
||||
.footer {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
color: #888;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.link {
|
||||
word-break: break-all;
|
||||
color: #4A90E2;
|
||||
}
|
||||
.warning {
|
||||
color: #E74C3C;
|
||||
padding: 10px;
|
||||
background-color: #FADBD8;
|
||||
border-radius: 4px;
|
||||
margin-top: 20px;
|
||||
.content {
|
||||
padding: 18px 8px 16px 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% block additional_styles %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>{% block header %}Evo AI{% endblock %}</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>{% block footer_message %}This is an automated email, please do not reply.{% endblock %}</p>
|
||||
<p>© {{ current_year }} Evo AI. All rights reserved.</p>
|
||||
</div>
|
||||
<div class="header">
|
||||
<h1>{% block header %}Evo AI{% endblock %}</h1>
|
||||
</div>
|
||||
<div class="content">{% block content %}{% endblock %}</div>
|
||||
<div class="footer">
|
||||
<p>
|
||||
{% block footer_message %}This is an automated email, please do not
|
||||
reply.{% endblock %}
|
||||
</p>
|
||||
<p>© {{ current_year }} Evo AI. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: a2a_utils.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from src.schemas.a2a_types import (
|
||||
ContentTypeNotSupportedError,
|
||||
JSONRPCResponse,
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: crypto.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: logger.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: otel.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import os
|
||||
import base64
|
||||
from src.config.settings import settings
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: security.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
from passlib.context import CryptContext
|
||||
from datetime import datetime, timedelta
|
||||
import secrets
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
"""
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: streaming.py │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import AsyncGenerator
|
||||
from fastapi import HTTPException
|
||||
|
||||
Reference in New Issue
Block a user