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
|
- **LangGraph**: Framework for building stateful, multi-agent workflows
|
||||||
- **ReactFlow**: Library for building node-based visual 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
|
## 🤖 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:
|
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:
|
You'll also need the following accounts/API keys:
|
||||||
|
|
||||||
- **OpenAI API Key**: Or API key from another AI provider
|
|
||||||
- **SendGrid Account**: For email functionality
|
- **SendGrid Account**: For email functionality
|
||||||
- **Google API Key**: If using Google's A2A protocol implementation
|
|
||||||
|
|
||||||
## 📋 Requirements
|
## 📋 Requirements
|
||||||
|
|
||||||
- Python 3.10+
|
- Python 3.10+
|
||||||
- PostgreSQL
|
- PostgreSQL
|
||||||
- Redis
|
- Redis
|
||||||
- OpenAI API Key (or other AI provider)
|
|
||||||
- SendGrid Account (for email sending)
|
- SendGrid Account (for email sending)
|
||||||
|
|
||||||
## 🔧 Installation
|
## 🔧 Installation
|
||||||
@@ -398,7 +440,7 @@ You'll also need the following accounts/API keys:
|
|||||||
1. Clone the repository:
|
1. Clone the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/your-username/evo-ai.git
|
git clone https://github.com/EvolutionAPI/evo-ai.git
|
||||||
cd evo-ai
|
cd evo-ai
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -446,6 +488,21 @@ make alembic-upgrade
|
|||||||
make seed-all
|
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
|
## 🚀 Getting Started
|
||||||
|
|
||||||
After installation, follow these steps to set up your first agent:
|
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`
|
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
|
## 👨💻 Development Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -891,3 +900,53 @@ GET /api/v1/agents?folder_id=folder-uuid
|
|||||||
Authorization: Bearer your-token-jwt
|
Authorization: Bearer your-token-jwt
|
||||||
x-client-id: client-uuid
|
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
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
@@ -22,11 +51,11 @@ TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engin
|
|||||||
def db_session():
|
def db_session():
|
||||||
"""Creates a fresh database session for each test."""
|
"""Creates a fresh database session for each test."""
|
||||||
Base.metadata.create_all(bind=engine) # Create tables
|
Base.metadata.create_all(bind=engine) # Create tables
|
||||||
|
|
||||||
connection = engine.connect()
|
connection = engine.connect()
|
||||||
transaction = connection.begin()
|
transaction = connection.begin()
|
||||||
session = TestingSessionLocal(bind=connection)
|
session = TestingSessionLocal(bind=connection)
|
||||||
|
|
||||||
# Use our test database instead of the standard one
|
# Use our test database instead of the standard one
|
||||||
def override_get_db():
|
def override_get_db():
|
||||||
try:
|
try:
|
||||||
@@ -34,11 +63,11 @@ def db_session():
|
|||||||
session.commit()
|
session.commit()
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
app.dependency_overrides[get_db] = override_get_db
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
|
|
||||||
yield session # The test will run here
|
yield session # The test will run here
|
||||||
|
|
||||||
# Teardown
|
# Teardown
|
||||||
transaction.rollback()
|
transaction.rollback()
|
||||||
connection.close()
|
connection.close()
|
||||||
@@ -50,4 +79,4 @@ def db_session():
|
|||||||
def client(db_session):
|
def client(db_session):
|
||||||
"""Creates a FastAPI TestClient with database session fixture."""
|
"""Creates a FastAPI TestClient with database session fixture."""
|
||||||
with TestClient(app) as test_client:
|
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"
|
description = "API for executing AI agents"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "EvoAI Team", email = "admin@evoai.com"}
|
{name = "Davidson Gomes", email = "contato@evolution-api.com"}
|
||||||
]
|
]
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
license = {text = "Proprietary"}
|
license = {text = "Apache-2.0"}
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"License :: Other/Proprietary License",
|
"License :: OSI Approved :: Apache Software License",
|
||||||
"Operating System :: OS Independent",
|
"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.
|
Main script to run all seeders in sequence.
|
||||||
Checks dependencies between seeders and runs them in the correct order.
|
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:
|
Script to create an initial admin user:
|
||||||
- Email: admin@evoai.com
|
- 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:
|
Script to create a demo client:
|
||||||
- Name: 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:
|
Script to create default MCP servers:
|
||||||
- Brave Search
|
- 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:
|
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.
|
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 typing import List
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
||||||
from sqlalchemy.orm import Session
|
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 fastapi import APIRouter, Depends, HTTPException, status, Header, Query
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.config.database import get_db
|
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 fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.config.database import get_db
|
from src.config.database import get_db
|
||||||
@@ -54,8 +83,7 @@ async def register_user(user_data: UserCreate, db: Session = Depends(get_db)):
|
|||||||
Raises:
|
Raises:
|
||||||
HTTPException: If there is an error in registration
|
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=False)
|
||||||
user, message = create_user(db, user_data, is_admin=False, auto_verify=True)
|
|
||||||
if not user:
|
if not user:
|
||||||
logger.error(f"Error registering user: {message}")
|
logger.error(f"Error registering user: {message}")
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=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:
|
Raises:
|
||||||
HTTPException: If credentials are invalid
|
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:
|
if not user:
|
||||||
logger.warning(f"Login attempt with invalid credentials: {form_data.email}")
|
if reason == "user_not_found" or reason == "invalid_password":
|
||||||
raise HTTPException(
|
logger.warning(f"Login attempt with invalid credentials: {form_data.email}")
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
raise HTTPException(
|
||||||
detail="Invalid email or password",
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
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)
|
access_token = create_access_token(user)
|
||||||
logger.info(f"Login successful for user: {user.email}")
|
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 (
|
from fastapi import (
|
||||||
APIRouter,
|
APIRouter,
|
||||||
Depends,
|
Depends,
|
||||||
@@ -55,8 +84,8 @@ async def websocket_chat(
|
|||||||
auth_data = await websocket.receive_json()
|
auth_data = await websocket.receive_json()
|
||||||
logger.info(f"Received authentication data: {auth_data}")
|
logger.info(f"Received authentication data: {auth_data}")
|
||||||
|
|
||||||
if not auth_data.get("type") == "authorization" or not auth_data.get(
|
if not (
|
||||||
"token"
|
auth_data.get("type") == "authorization" and auth_data.get("token")
|
||||||
):
|
):
|
||||||
logger.warning("Invalid authentication message")
|
logger.warning("Invalid authentication message")
|
||||||
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
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)
|
await verify_user_client(payload, db, agent.client_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
final_response_text = await run_agent(
|
final_response = await run_agent(
|
||||||
request.agent_id,
|
request.agent_id,
|
||||||
request.external_id,
|
request.external_id,
|
||||||
request.message,
|
request.message,
|
||||||
@@ -170,14 +199,15 @@ async def chat(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"response": final_response_text,
|
"response": final_response["final_response"],
|
||||||
|
"message_history": final_response["message_history"],
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"timestamp": datetime.now().isoformat(),
|
"timestamp": datetime.now().isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
except AgentNotFoundError as e:
|
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:
|
except Exception as e:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
|
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 fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from pydantic import BaseModel, EmailStr
|
from pydantic import BaseModel, EmailStr
|
||||||
from sqlalchemy.orm import Session
|
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 fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.config.database import get_db
|
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 fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.config.database import get_db
|
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 fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.config.database import get_db
|
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 import create_engine
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker
|
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.
|
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
|
import os
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from pydantic_settings import BaseSettings
|
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 fastapi import HTTPException
|
||||||
from typing import Optional, Dict, Any
|
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 import HTTPException, Depends, status
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
from jose import JWTError, jwt
|
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 os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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
|
import os
|
||||||
from sqlalchemy import (
|
from sqlalchemy import (
|
||||||
Column,
|
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 datetime import datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Annotated, Any, Literal
|
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 typing import List, Optional, Dict, Union, Any
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from uuid import UUID
|
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 pydantic import BaseModel, Field
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
from datetime import datetime
|
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 pydantic import BaseModel, Field
|
||||||
from typing import Dict, Any, Optional
|
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 pydantic import BaseModel, Field, validator, UUID4, ConfigDict
|
||||||
from typing import Optional, Dict, Any, List
|
from typing import Optional, Dict, Any, List
|
||||||
from datetime import datetime
|
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 datetime import datetime
|
||||||
from typing import Dict, List, Optional, Any, Literal
|
from typing import Dict, List, Optional, Any, Literal
|
||||||
from pydantic import BaseModel, Field
|
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 pydantic import BaseModel, EmailStr, Field
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from datetime import datetime
|
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 import BaseAgent
|
||||||
from google.adk.agents.invocation_context import InvocationContext
|
from google.adk.agents.invocation_context import InvocationContext
|
||||||
from google.adk.events import Event
|
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 logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import AsyncIterable
|
from collections.abc import AsyncIterable
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
import json
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@@ -236,29 +266,31 @@ class A2ATaskManager:
|
|||||||
) -> JSONRPCResponse:
|
) -> JSONRPCResponse:
|
||||||
"""Processes a task using the specified agent."""
|
"""Processes a task using the specified agent."""
|
||||||
task_params = request.params
|
task_params = request.params
|
||||||
query = self._extract_user_query(task_params)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Process the query with the agent
|
query = self._extract_user_query(task_params)
|
||||||
result = await self._run_agent(agent, query, task_params.sessionId)
|
result_obj = await self._run_agent(agent, query, task_params.sessionId)
|
||||||
|
|
||||||
# Create the response part
|
all_messages = await self._extract_messages_from_history(
|
||||||
text_part = {"type": "text", "text": result}
|
result_obj.get("message_history", [])
|
||||||
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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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 = await self.update_store(
|
||||||
task_params.id,
|
task_params.id,
|
||||||
TaskStatus(state=task_state, message=agent_message),
|
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)
|
return SendTaskResponse(id=request.id, result=task)
|
||||||
@@ -269,12 +301,73 @@ class A2ATaskManager:
|
|||||||
error=InternalError(message=f"Error processing task: {str(e)}"),
|
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(
|
async def _stream_task_process(
|
||||||
self, request: SendTaskStreamingRequest, agent: Agent
|
self, request: SendTaskStreamingRequest, agent: Agent
|
||||||
) -> AsyncIterable[SendTaskStreamingResponse]:
|
) -> AsyncIterable[SendTaskStreamingResponse]:
|
||||||
"""Processes a task in streaming mode using the specified agent."""
|
"""Processes a task in streaming mode using the specified agent."""
|
||||||
task_params = request.params
|
query = self._extract_user_query(request.params)
|
||||||
query = self._extract_user_query(task_params)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Send initial processing status
|
# Send initial processing status
|
||||||
@@ -286,14 +379,14 @@ class A2ATaskManager:
|
|||||||
|
|
||||||
# Update the task with the processing message and inform the WORKING state
|
# Update the task with the processing message and inform the WORKING state
|
||||||
await self.update_store(
|
await self.update_store(
|
||||||
task_params.id,
|
request.params.id,
|
||||||
TaskStatus(state=TaskState.WORKING, message=processing_message),
|
TaskStatus(state=TaskState.WORKING, message=processing_message),
|
||||||
)
|
)
|
||||||
|
|
||||||
yield SendTaskStreamingResponse(
|
yield SendTaskStreamingResponse(
|
||||||
id=request.id,
|
id=request.id,
|
||||||
result=TaskStatusUpdateEvent(
|
result=TaskStatusUpdateEvent(
|
||||||
id=task_params.id,
|
id=request.params.id,
|
||||||
status=TaskStatus(
|
status=TaskStatus(
|
||||||
state=TaskState.WORKING,
|
state=TaskState.WORKING,
|
||||||
message=processing_message,
|
message=processing_message,
|
||||||
@@ -302,11 +395,11 @@ class A2ATaskManager:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Collect the chunks of the agent's response
|
external_id = request.params.sessionId
|
||||||
external_id = task_params.sessionId
|
|
||||||
full_response = ""
|
full_response = ""
|
||||||
|
|
||||||
# We use the same streaming function used in the WebSocket
|
final_message = None
|
||||||
|
|
||||||
async for chunk in run_agent_stream(
|
async for chunk in run_agent_stream(
|
||||||
agent_id=str(agent.id),
|
agent_id=str(agent.id),
|
||||||
external_id=external_id,
|
external_id=external_id,
|
||||||
@@ -316,47 +409,78 @@ class A2ATaskManager:
|
|||||||
memory_service=memory_service,
|
memory_service=memory_service,
|
||||||
db=self.db,
|
db=self.db,
|
||||||
):
|
):
|
||||||
# Send incremental progress updates
|
try:
|
||||||
update_text_part = {"type": "text", "text": chunk}
|
chunk_data = json.loads(chunk)
|
||||||
update_message = Message(role="agent", parts=[update_text_part])
|
except Exception as e:
|
||||||
|
logger.warning(f"Invalid chunk received: {chunk} - {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
# Update the task with each intermediate message
|
if (
|
||||||
await self.update_store(
|
isinstance(chunk_data, dict)
|
||||||
task_params.id,
|
and "type" in chunk_data
|
||||||
TaskStatus(state=TaskState.WORKING, message=update_message),
|
and chunk_data["type"]
|
||||||
)
|
in [
|
||||||
|
"history",
|
||||||
|
"history_update",
|
||||||
|
"history_complete",
|
||||||
|
]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
yield SendTaskStreamingResponse(
|
if isinstance(chunk_data, dict):
|
||||||
id=request.id,
|
if "type" not in chunk_data and "text" in chunk_data:
|
||||||
result=TaskStatusUpdateEvent(
|
chunk_data["type"] = "text"
|
||||||
id=task_params.id,
|
|
||||||
status=TaskStatus(
|
|
||||||
state=TaskState.WORKING,
|
|
||||||
message=update_message,
|
|
||||||
),
|
|
||||||
final=False,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
full_response += chunk
|
|
||||||
|
|
||||||
# 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 = (
|
task_state = (
|
||||||
TaskState.INPUT_REQUIRED
|
TaskState.INPUT_REQUIRED
|
||||||
if "MISSING_INFO:" in full_response
|
if "MISSING_INFO:" in full_response
|
||||||
else TaskState.COMPLETED
|
else TaskState.COMPLETED
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create the final response part
|
# Create the final response if we don't have one yet
|
||||||
final_text_part = {"type": "text", "text": full_response}
|
if not final_message:
|
||||||
parts = [final_text_part]
|
final_text_part = {"type": "text", "text": full_response}
|
||||||
final_message = Message(role="agent", parts=parts)
|
parts = [final_text_part]
|
||||||
|
final_message = Message(role="agent", parts=parts)
|
||||||
|
|
||||||
# Create the final artifact from the final response
|
final_artifact = Artifact(parts=final_message.parts, index=0)
|
||||||
final_artifact = Artifact(parts=parts, index=0)
|
|
||||||
|
|
||||||
# Update the task in the store with the final response
|
task = await self.update_store(
|
||||||
await self.update_store(
|
request.params.id,
|
||||||
task_params.id,
|
|
||||||
TaskStatus(state=task_state, message=final_message),
|
TaskStatus(state=task_state, message=final_message),
|
||||||
[final_artifact],
|
[final_artifact],
|
||||||
)
|
)
|
||||||
@@ -365,7 +489,7 @@ class A2ATaskManager:
|
|||||||
yield SendTaskStreamingResponse(
|
yield SendTaskStreamingResponse(
|
||||||
id=request.id,
|
id=request.id,
|
||||||
result=TaskArtifactUpdateEvent(
|
result=TaskArtifactUpdateEvent(
|
||||||
id=task_params.id, artifact=final_artifact
|
id=request.params.id, artifact=final_artifact
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -373,7 +497,7 @@ class A2ATaskManager:
|
|||||||
yield SendTaskStreamingResponse(
|
yield SendTaskStreamingResponse(
|
||||||
id=request.id,
|
id=request.id,
|
||||||
result=TaskStatusUpdateEvent(
|
result=TaskStatusUpdateEvent(
|
||||||
id=task_params.id,
|
id=request.params.id,
|
||||||
status=TaskStatus(state=task_state),
|
status=TaskStatus(state=task_state),
|
||||||
final=True,
|
final=True,
|
||||||
),
|
),
|
||||||
@@ -390,6 +514,7 @@ class A2ATaskManager:
|
|||||||
task_id: str,
|
task_id: str,
|
||||||
status: TaskStatus,
|
status: TaskStatus,
|
||||||
artifacts: Optional[list[Artifact]] = None,
|
artifacts: Optional[list[Artifact]] = None,
|
||||||
|
update_history: bool = True,
|
||||||
) -> Task:
|
) -> Task:
|
||||||
"""Updates the status and artifacts of a task."""
|
"""Updates the status and artifacts of a task."""
|
||||||
async with self.lock:
|
async with self.lock:
|
||||||
@@ -399,8 +524,8 @@ class A2ATaskManager:
|
|||||||
task = self.tasks[task_id]
|
task = self.tasks[task_id]
|
||||||
task.status = status
|
task.status = status
|
||||||
|
|
||||||
# Add message to history if it exists
|
# Add message to history if it exists and update_history is True
|
||||||
if status.message is not None:
|
if status.message is not None and update_history:
|
||||||
if task.history is None:
|
if task.history is None:
|
||||||
task.history = []
|
task.history = []
|
||||||
task.history.append(status.message)
|
task.history.append(status.message)
|
||||||
@@ -423,27 +548,22 @@ class A2ATaskManager:
|
|||||||
|
|
||||||
return part.text
|
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."""
|
"""Executes the agent to process the user query."""
|
||||||
try:
|
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
|
# We call the same function used in the chat API
|
||||||
final_response = await run_agent(
|
return await run_agent(
|
||||||
agent_id=str(agent.id),
|
agent_id=str(agent.id),
|
||||||
external_id=external_id,
|
external_id=session_id,
|
||||||
message=query,
|
message=query,
|
||||||
session_service=session_service,
|
session_service=session_service,
|
||||||
artifacts_service=artifacts_service,
|
artifacts_service=artifacts_service,
|
||||||
memory_service=memory_service,
|
memory_service=memory_service,
|
||||||
db=self.db,
|
db=self.db,
|
||||||
)
|
)
|
||||||
|
|
||||||
return final_response
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error running agent: {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:
|
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."""
|
"""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()
|
new_task = task.model_copy()
|
||||||
|
|
||||||
# Limit the history if requested
|
# 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:
|
if history_length > 0:
|
||||||
new_task.history = (
|
if len(new_task.history) > history_length:
|
||||||
new_task.history[-history_length:] if new_task.history else []
|
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:
|
else:
|
||||||
new_task.history = []
|
new_task.history = []
|
||||||
|
|
||||||
@@ -511,112 +636,11 @@ class A2AService:
|
|||||||
if not agent:
|
if not agent:
|
||||||
raise ValueError(f"Agent {agent_id} not found")
|
raise ValueError(f"Agent {agent_id} not found")
|
||||||
|
|
||||||
# Build the agent card based on the agent's information
|
capabilities = AgentCapabilities(
|
||||||
capabilities = AgentCapabilities(streaming=True)
|
streaming=True, pushNotifications=False, stateTransitionHistory=True
|
||||||
|
)
|
||||||
|
|
||||||
# List to store all skills
|
skills = self._get_agent_skills(agent)
|
||||||
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']}")
|
|
||||||
|
|
||||||
card = AgentCard(
|
card = AgentCard(
|
||||||
name=agent.name,
|
name=agent.name,
|
||||||
@@ -639,3 +663,134 @@ class A2AService:
|
|||||||
|
|
||||||
logger.info(f"Generated agent card with {len(skills)} skills")
|
logger.info(f"Generated agent card with {len(skills)} skills")
|
||||||
return card
|
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 typing import List, Optional, Tuple
|
||||||
from google.adk.agents.llm_agent import LlmAgent
|
from google.adk.agents.llm_agent import LlmAgent
|
||||||
from google.adk.agents import SequentialAgent, ParallelAgent, LoopAgent, BaseAgent
|
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.adk.runners import Runner
|
||||||
from google.genai.types import Content, Part
|
from google.genai.types import Content, Part
|
||||||
from google.adk.sessions import DatabaseSessionService
|
from google.adk.sessions import DatabaseSessionService
|
||||||
@@ -65,7 +94,7 @@ async def run_agent(
|
|||||||
artifact_service=artifacts_service,
|
artifact_service=artifacts_service,
|
||||||
memory_service=memory_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:
|
if session_id is None:
|
||||||
session_id = adk_session_id
|
session_id = adk_session_id
|
||||||
|
|
||||||
@@ -88,6 +117,8 @@ async def run_agent(
|
|||||||
logger.info("Starting agent execution")
|
logger.info("Starting agent execution")
|
||||||
|
|
||||||
final_response_text = "No final response captured."
|
final_response_text = "No final response captured."
|
||||||
|
message_history = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response_queue = asyncio.Queue()
|
response_queue = asyncio.Queue()
|
||||||
execution_completed = asyncio.Event()
|
execution_completed = asyncio.Event()
|
||||||
@@ -104,6 +135,11 @@ async def run_agent(
|
|||||||
all_responses = []
|
all_responses = []
|
||||||
|
|
||||||
async for event in events_async:
|
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 (
|
if (
|
||||||
event.content
|
event.content
|
||||||
and event.content.parts
|
and event.content.parts
|
||||||
@@ -176,10 +212,13 @@ async def run_agent(
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing request: {str(e)}")
|
logger.error(f"Error processing request: {str(e)}")
|
||||||
raise e
|
raise InternalServerError(str(e)) from e
|
||||||
|
|
||||||
logger.info("Agent execution completed successfully")
|
logger.info("Agent execution completed successfully")
|
||||||
return final_response_text
|
return {
|
||||||
|
"final_response": final_response_text,
|
||||||
|
"message_history": message_history,
|
||||||
|
}
|
||||||
except AgentNotFoundError as e:
|
except AgentNotFoundError as e:
|
||||||
logger.error(f"Error processing request: {str(e)}")
|
logger.error(f"Error processing request: {str(e)}")
|
||||||
raise e
|
raise e
|
||||||
@@ -256,7 +295,7 @@ async def run_agent_stream(
|
|||||||
artifact_service=artifacts_service,
|
artifact_service=artifacts_service,
|
||||||
memory_service=memory_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:
|
if session_id is None:
|
||||||
session_id = adk_session_id
|
session_id = adk_session_id
|
||||||
|
|
||||||
@@ -286,9 +325,51 @@ async def run_agent_stream(
|
|||||||
)
|
)
|
||||||
|
|
||||||
async for event in events_async:
|
async for event in events_async:
|
||||||
event_dict = event.dict()
|
try:
|
||||||
event_dict = convert_sets(event_dict)
|
event_dict = event.dict()
|
||||||
yield json.dumps(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(
|
completed_session = session_service.get_session(
|
||||||
app_name=agent_id,
|
app_name=agent_id,
|
||||||
@@ -299,7 +380,7 @@ async def run_agent_stream(
|
|||||||
memory_service.add_session_to_memory(completed_session)
|
memory_service.add_session_to_memory(completed_session)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing request: {str(e)}")
|
logger.error(f"Error processing request: {str(e)}")
|
||||||
raise e
|
raise InternalServerError(str(e)) from e
|
||||||
finally:
|
finally:
|
||||||
# Clean up MCP connection
|
# Clean up MCP connection
|
||||||
if exit_stack:
|
if exit_stack:
|
||||||
@@ -312,7 +393,7 @@ async def run_agent_stream(
|
|||||||
logger.info("Agent streaming execution completed successfully")
|
logger.info("Agent streaming execution completed successfully")
|
||||||
except AgentNotFoundError as e:
|
except AgentNotFoundError as e:
|
||||||
logger.error(f"Error processing request: {str(e)}")
|
logger.error(f"Error processing request: {str(e)}")
|
||||||
raise e
|
raise InternalServerError(str(e)) from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Internal error processing request: {str(e)}", exc_info=True
|
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.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from fastapi import HTTPException, status
|
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.models.models import ApiKey
|
||||||
from src.utils.crypto import encrypt_api_key, decrypt_api_key
|
from src.utils.crypto import encrypt_api_key, decrypt_api_key
|
||||||
from sqlalchemy.orm import Session
|
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.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from src.models.models import AuditLog
|
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 sqlalchemy.orm import Session
|
||||||
from src.models.models import User
|
from src.models.models import User
|
||||||
from src.schemas.user import TokenData
|
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.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from fastapi import HTTPException, status
|
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 typing import Any, Dict, List
|
||||||
from google.adk.tools import FunctionTool
|
from google.adk.tools import FunctionTool
|
||||||
import requests
|
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
|
import sendgrid
|
||||||
from sendgrid.helpers.mail import Mail, Email, To, Content
|
from sendgrid.helpers.mail import Mail, Email, To, Content
|
||||||
from src.config.settings import settings
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
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
|
bool: True if the email was sent successfully, False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||||
from_email = Email(settings.EMAIL_FROM)
|
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||||
to_email = To(email)
|
to_email = To(email)
|
||||||
subject = "Email Verification - Evo AI"
|
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(
|
html_content = _render_template(
|
||||||
"verification_email",
|
"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
|
bool: True if the email was sent successfully, False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||||
from_email = Email(settings.EMAIL_FROM)
|
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||||
to_email = To(email)
|
to_email = To(email)
|
||||||
subject = "Password Reset - Evo AI"
|
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(
|
html_content = _render_template(
|
||||||
"password_reset",
|
"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
|
bool: True if the email was sent successfully, False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||||
from_email = Email(settings.EMAIL_FROM)
|
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||||
to_email = To(email)
|
to_email = To(email)
|
||||||
subject = "Welcome to Evo AI"
|
subject = "Welcome to Evo AI"
|
||||||
|
|
||||||
dashboard_link = f"{settings.APP_URL}/dashboard"
|
dashboard_link = f"{os.getenv('APP_URL')}/dashboard"
|
||||||
|
|
||||||
html_content = _render_template(
|
html_content = _render_template(
|
||||||
"welcome_email",
|
"welcome_email",
|
||||||
@@ -200,12 +228,14 @@ def send_account_locked_email(
|
|||||||
bool: True if the email was sent successfully, False otherwise
|
bool: True if the email was sent successfully, False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
sg = sendgrid.SendGridAPIClient(api_key=settings.SENDGRID_API_KEY)
|
sg = sendgrid.SendGridAPIClient(api_key=os.getenv("SENDGRID_API_KEY"))
|
||||||
from_email = Email(settings.EMAIL_FROM)
|
from_email = Email(os.getenv("EMAIL_FROM"))
|
||||||
to_email = To(email)
|
to_email = To(email)
|
||||||
subject = "Security Alert - Account Locked"
|
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(
|
html_content = _render_template(
|
||||||
"account_locked",
|
"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.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from fastapi import HTTPException, status
|
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 typing import Any, Dict, List, Optional, Tuple
|
||||||
from google.adk.tools.mcp_tool.mcp_toolset import (
|
from google.adk.tools.mcp_tool.mcp_toolset import (
|
||||||
MCPToolset,
|
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 src.config.settings import settings
|
||||||
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
|
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
|
||||||
from google.adk.sessions import DatabaseSessionService
|
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 google.adk.sessions import DatabaseSessionService
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.models.models import Session as SessionModel
|
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.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from fastapi import HTTPException, status
|
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.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from src.models.models import User, Client
|
from src.models.models import User, Client
|
||||||
@@ -7,7 +36,7 @@ from src.services.email_service import (
|
|||||||
send_verification_email,
|
send_verification_email,
|
||||||
send_password_reset_email,
|
send_password_reset_email,
|
||||||
)
|
)
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
import uuid
|
import uuid
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Tuple
|
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"
|
return False, "Invalid password reset token"
|
||||||
|
|
||||||
# Check if the token has expired
|
# 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(
|
logger.warning(
|
||||||
f"Attempt to reset password with expired token for user: {user.email}"
|
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
|
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
|
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
|
password: User password
|
||||||
|
|
||||||
Returns:
|
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)
|
user = get_user_by_email(db, email)
|
||||||
if not user:
|
if not user:
|
||||||
return None
|
return None, "user_not_found"
|
||||||
if not verify_password(password, user.password_hash):
|
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:
|
if not user.is_active:
|
||||||
return None
|
return None, "inactive_user"
|
||||||
return user
|
return user, "success"
|
||||||
|
|
||||||
|
|
||||||
def get_admin_users(db: Session, skip: int = 0, limit: int = 100):
|
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 datetime import datetime
|
||||||
from google.adk.agents import BaseAgent
|
from google.adk.agents import BaseAgent
|
||||||
from google.adk.agents.invocation_context import InvocationContext
|
from google.adk.agents.invocation_context import InvocationContext
|
||||||
@@ -87,7 +116,7 @@ class WorkflowAgent(BaseAgent):
|
|||||||
if not content:
|
if not content:
|
||||||
content = [
|
content = [
|
||||||
Event(
|
Event(
|
||||||
author="agent",
|
author="workflow_agent",
|
||||||
content=Content(parts=[Part(text="Content not found")]),
|
content=Content(parts=[Part(text="Content not found")]),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -139,7 +168,7 @@ class WorkflowAgent(BaseAgent):
|
|||||||
yield {
|
yield {
|
||||||
"content": [
|
"content": [
|
||||||
Event(
|
Event(
|
||||||
author="agent",
|
author="workflow_agent",
|
||||||
content=Content(parts=[Part(text="Agent not found")]),
|
content=Content(parts=[Part(text="Agent not found")]),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -162,7 +191,7 @@ class WorkflowAgent(BaseAgent):
|
|||||||
conversation_history.append(event)
|
conversation_history.append(event)
|
||||||
new_content.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 = state.get("node_outputs", {})
|
||||||
node_outputs[node_id] = {
|
node_outputs[node_id] = {
|
||||||
@@ -252,7 +281,7 @@ class WorkflowAgent(BaseAgent):
|
|||||||
|
|
||||||
condition_content = [
|
condition_content = [
|
||||||
Event(
|
Event(
|
||||||
author="agent",
|
author="workflow_agent",
|
||||||
content=Content(parts=[Part(text="Cycle limit reached")]),
|
content=Content(parts=[Part(text="Cycle limit reached")]),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -283,7 +312,7 @@ class WorkflowAgent(BaseAgent):
|
|||||||
|
|
||||||
condition_content = [
|
condition_content = [
|
||||||
Event(
|
Event(
|
||||||
author="agent",
|
author=label,
|
||||||
content=Content(
|
content=Content(
|
||||||
parts=[
|
parts=[
|
||||||
Part(
|
Part(
|
||||||
@@ -317,8 +346,10 @@ class WorkflowAgent(BaseAgent):
|
|||||||
session_id = state.get("session_id", "")
|
session_id = state.get("session_id", "")
|
||||||
conversation_history = state.get("conversation_history", [])
|
conversation_history = state.get("conversation_history", [])
|
||||||
|
|
||||||
|
label = node_data.get("label", "message_node")
|
||||||
|
|
||||||
new_event = Event(
|
new_event = Event(
|
||||||
author="agent",
|
author=label,
|
||||||
content=Content(parts=[Part(text=message_content)]),
|
content=Content(parts=[Part(text=message_content)]),
|
||||||
)
|
)
|
||||||
content = content + [new_event]
|
content = content + [new_event]
|
||||||
@@ -351,139 +382,160 @@ class WorkflowAgent(BaseAgent):
|
|||||||
condition_data = condition.get("data", {})
|
condition_data = condition.get("data", {})
|
||||||
|
|
||||||
if condition_type == "previous-output":
|
if condition_type == "previous-output":
|
||||||
field = condition_data.get("field")
|
field, operator, expected_value, actual_value = (
|
||||||
operator = condition_data.get("operator")
|
self._extract_condition_values(condition_data, state)
|
||||||
expected_value = condition_data.get("value")
|
)
|
||||||
|
|
||||||
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:
|
if field == "content" and isinstance(actual_value, list) and actual_value:
|
||||||
# Extract text from each event for comparison
|
actual_value = self._extract_text_from_events(actual_value)
|
||||||
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)
|
|
||||||
|
|
||||||
if extracted_texts:
|
result = self._process_condition(operator, actual_value, expected_value)
|
||||||
actual_value = " ".join(extracted_texts)
|
|
||||||
print(f" Extracted text from events: '{actual_value[:100]}...'")
|
|
||||||
|
|
||||||
# Convert values to string for easier comparisons
|
print(f" Check '{operator}': {result}")
|
||||||
if actual_value is not None:
|
return result
|
||||||
actual_str = str(actual_value)
|
|
||||||
else:
|
|
||||||
actual_str = ""
|
|
||||||
|
|
||||||
if expected_value is not None:
|
return False
|
||||||
expected_str = str(expected_value)
|
|
||||||
else:
|
|
||||||
expected_str = ""
|
|
||||||
|
|
||||||
# Checks for definition
|
def _process_condition(self, operator, actual_value, expected_value):
|
||||||
if operator == "is_defined":
|
"""Converts values to strings and processes the condition using the appropriate operator."""
|
||||||
result = actual_value is not None and actual_value != ""
|
actual_str = str(actual_value) if actual_value is not None else ""
|
||||||
print(f" Check '{operator}': {result}")
|
expected_str = str(expected_value) if expected_value is not None else ""
|
||||||
return result
|
|
||||||
elif operator == "is_not_defined":
|
|
||||||
result = actual_value is None or actual_value == ""
|
|
||||||
print(f" Check '{operator}': {result}")
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Checks for equality
|
return self._process_operator(operator, actual_value, actual_str, expected_str)
|
||||||
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
|
|
||||||
|
|
||||||
# Checks for content
|
def _extract_condition_values(self, condition_data, state):
|
||||||
elif operator == "contains":
|
"""Extracts field, operator, expected value and actual value from condition data."""
|
||||||
# Convert both to lowercase for case-insensitive comparison
|
field = condition_data.get("field")
|
||||||
expected_lower = expected_str.lower()
|
operator = condition_data.get("operator")
|
||||||
actual_lower = actual_str.lower()
|
expected_value = condition_data.get("value")
|
||||||
print(
|
actual_value = state.get(field, "")
|
||||||
f" Comparison 'contains' without case distinction: '{expected_lower}' in '{actual_lower[:100]}...'"
|
|
||||||
|
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
|
if extracted_texts:
|
||||||
elif operator == "starts_with":
|
joined_text = " ".join(extracted_texts)
|
||||||
result = actual_str.lower().startswith(expected_str.lower())
|
print(f" Extracted text from events: '{joined_text[:100]}...'")
|
||||||
print(f" Check '{operator}': {result}")
|
return joined_text
|
||||||
return result
|
|
||||||
elif operator == "ends_with":
|
|
||||||
result = actual_str.lower().endswith(expected_str.lower())
|
|
||||||
print(f" Check '{operator}': {result}")
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Numeric checks (attempting to convert to number)
|
return ""
|
||||||
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
|
|
||||||
|
|
||||||
if operator == "greater_than":
|
def _process_operator(self, operator, actual_value, actual_str, expected_str):
|
||||||
result = actual_num > expected_num
|
"""Process the operator and return the result of the comparison."""
|
||||||
elif operator == "greater_than_or_equal":
|
# Definition checks
|
||||||
result = actual_num >= expected_num
|
if operator in ["is_defined", "is_not_defined"]:
|
||||||
elif operator == "less_than":
|
return self._check_definition(operator, actual_value)
|
||||||
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
|
|
||||||
|
|
||||||
# Checks with regular expressions
|
# Equality checks
|
||||||
elif operator == "matches":
|
elif operator in ["equals", "not_equals"]:
|
||||||
import re
|
return self._check_equality(operator, actual_str, expected_str)
|
||||||
|
|
||||||
try:
|
# Content checks
|
||||||
pattern = re.compile(expected_str, re.IGNORECASE)
|
elif operator in ["contains", "not_contains"]:
|
||||||
result = bool(pattern.search(actual_str))
|
return self._case_insensitive_comparison(expected_str, actual_str, operator)
|
||||||
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
|
|
||||||
|
|
||||||
try:
|
# String pattern checks
|
||||||
pattern = re.compile(expected_str, re.IGNORECASE)
|
elif operator in ["starts_with", "ends_with"]:
|
||||||
result = not bool(pattern.search(actual_str))
|
return self._check_string_pattern(operator, actual_str, expected_str)
|
||||||
print(f" Check '{operator}': {result}")
|
|
||||||
return result
|
# Numeric checks
|
||||||
except re.error:
|
elif operator in [
|
||||||
print(f" Error in regular expression: '{expected_str}'")
|
"greater_than",
|
||||||
return True # If the regex is invalid, we consider that there was no match
|
"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
|
return False
|
||||||
|
|
||||||
@@ -529,38 +581,15 @@ class WorkflowAgent(BaseAgent):
|
|||||||
conditions = condition_nodes[node_id]
|
conditions = condition_nodes[node_id]
|
||||||
any_condition_met = False
|
any_condition_met = False
|
||||||
|
|
||||||
for condition in conditions:
|
node_outputs = state.get("node_outputs", {})
|
||||||
condition_id = condition.get("id")
|
if node_id in node_outputs:
|
||||||
|
conditions_met = node_outputs[node_id].get("conditions_met", [])
|
||||||
# Get latest event for evaluation, ignoring condition node informational events
|
if conditions_met:
|
||||||
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:
|
|
||||||
any_condition_met = True
|
any_condition_met = True
|
||||||
|
condition_id = conditions_met[0]
|
||||||
print(
|
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 (
|
if (
|
||||||
node_id in edges_map
|
node_id in edges_map
|
||||||
and condition_id in edges_map[node_id]
|
and condition_id in edges_map[node_id]
|
||||||
@@ -568,8 +597,49 @@ class WorkflowAgent(BaseAgent):
|
|||||||
return edges_map[node_id][condition_id]
|
return edges_map[node_id][condition_id]
|
||||||
else:
|
else:
|
||||||
print(
|
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 no condition is met, use the bottom-handle if available
|
||||||
if not any_condition_met:
|
if not any_condition_met:
|
||||||
@@ -706,91 +776,96 @@ class WorkflowAgent(BaseAgent):
|
|||||||
async def _run_async_impl(
|
async def _run_async_impl(
|
||||||
self, ctx: InvocationContext
|
self, ctx: InvocationContext
|
||||||
) -> AsyncGenerator[Event, None]:
|
) -> AsyncGenerator[Event, None]:
|
||||||
"""
|
"""Implementation of the workflow agent executing the defined workflow and returning results."""
|
||||||
Implementation of the workflow agent.
|
|
||||||
|
|
||||||
This method follows the pattern of custom agent implementation,
|
|
||||||
executing the defined workflow and returning the results.
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 1. Extract the user message from the context
|
user_message = await self._extract_user_message(ctx)
|
||||||
user_message = None
|
session_id = self._get_session_id(ctx)
|
||||||
|
|
||||||
# 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
|
|
||||||
graph = await self._create_graph(ctx, self.flow_json)
|
graph = await self._create_graph(ctx, self.flow_json)
|
||||||
|
initial_state = await self._prepare_initial_state(
|
||||||
# 4. Prepare the initial state
|
ctx, user_message, session_id
|
||||||
user_event = Event(
|
|
||||||
author="user",
|
|
||||||
content=Content(parts=[Part(text=user_message)]),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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("\n🚀 Starting workflow execution:")
|
||||||
print(f"Initial content: {user_message[:100]}...")
|
print(f"Initial content: {user_message[:100]}...")
|
||||||
|
|
||||||
sent_events = 0 # Count of events already sent
|
# Iterar sobre o AsyncGenerator em vez de usar await
|
||||||
|
async for event in self._execute_workflow(ctx, graph, initial_state):
|
||||||
async for state in graph.astream(initial_state, {"recursion_limit": 20}):
|
yield event
|
||||||
# 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
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Handle any uncaught errors
|
yield await self._handle_workflow_error(e)
|
||||||
error_msg = f"Error executing the workflow agent: {str(e)}"
|
|
||||||
print(error_msg)
|
async def _extract_user_message(self, ctx: InvocationContext) -> str:
|
||||||
yield Event(
|
"""Extracts the user message from context session events or state."""
|
||||||
author=self.name,
|
# Try to find message in session events
|
||||||
content=Content(
|
if ctx.session and hasattr(ctx.session, "events") and ctx.session.events:
|
||||||
role="agent",
|
for event in reversed(ctx.session.events):
|
||||||
parts=[Part(text=error_msg)],
|
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>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{% block title %}Evo AI{% endblock %}</title>
|
<title>{% block title %}Evo AI{% endblock %}</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: "Segoe UI", Arial, sans-serif;
|
||||||
line-height: 1.6;
|
background-color: #f7f7f7;
|
||||||
margin: 0;
|
color: #222;
|
||||||
padding: 0;
|
margin: 0;
|
||||||
background-color: #f7f7f7;
|
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 {
|
.content {
|
||||||
max-width: 600px;
|
padding: 18px 8px 16px 8px;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% block additional_styles %}{% endblock %}
|
{% block additional_styles %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>{% block header %}Evo AI{% endblock %}</h1>
|
<h1>{% block header %}Evo AI{% endblock %}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">{% block content %}{% endblock %}</div>
|
||||||
{% block content %}{% endblock %}
|
<div class="footer">
|
||||||
</div>
|
<p>
|
||||||
<div class="footer">
|
{% block footer_message %}This is an automated email, please do not
|
||||||
<p>{% block footer_message %}This is an automated email, please do not reply.{% endblock %}</p>
|
reply.{% endblock %}
|
||||||
<p>© {{ current_year }} Evo AI. All rights reserved.</p>
|
</p>
|
||||||
</div>
|
<p>© {{ current_year }} Evo AI. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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 (
|
from src.schemas.a2a_types import (
|
||||||
ContentTypeNotSupportedError,
|
ContentTypeNotSupportedError,
|
||||||
JSONRPCResponse,
|
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
|
from cryptography.fernet import Fernet
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
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 logging
|
||||||
import os
|
import os
|
||||||
import sys
|
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 os
|
||||||
import base64
|
import base64
|
||||||
from src.config.settings import settings
|
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 passlib.context import CryptContext
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import secrets
|
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
|
import asyncio
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
|||||||
Reference in New Issue
Block a user