mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 00:02:03 +00:00
556b4a41a1
* wip: openai proxy and other ai proxy integration * fixing migration script * wip: support different ai provider in front, fix proxy openai * wip: adding frontend ai provider * updated copilot types * wip: working on anthropic integration * done AI proxy front * adding new type and support for anthropic * updating gitignore * adding streaming response * added streaming prompt * push lib/gen * wip: fixing anthropic * anthropic fully supported * fix backend missing var error and fully support stream event for anthropic * remove gen directory * fixing openapi file * add support for mistral, and update create workspace components * remove deref.json * remove package-json * openapi * fix ui enable code * added utility function for init workspace ai provider * fix workspace switch bug * update anthropic property and fixed frontend error * fix workspace settings * update error message and fix typo migration file * chore: update openapi file * fix dev file * add .sqlx * all * update sqlx --------- Co-authored-by: dieriba <t.dieriba@gmail.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
148 lines
4.3 KiB
Rust
148 lines
4.3 KiB
Rust
/*
|
|
* Author: Ruben Fiszel
|
|
* Copyright: Windmill Labs, Inc 2022
|
|
* This file and its contents are licensed under the AGPLv3 License.
|
|
* Please see the included NOTICE for copyright information and
|
|
* LICENSE-AGPL for a copy of the license.
|
|
*/
|
|
|
|
use axum::body::Body;
|
|
use axum::response::Response;
|
|
use axum::{response::IntoResponse, response::Json};
|
|
|
|
use hyper::StatusCode;
|
|
use sqlx::migrate::MigrateError;
|
|
use thiserror::Error;
|
|
use tokio::io;
|
|
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
pub type JsonResult<T> = std::result::Result<Json<T>, Error>;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum Error {
|
|
#[error("Uuid Error {0}")]
|
|
UuidErr(#[from] uuid::Error),
|
|
#[error("Bad config: {0}")]
|
|
BadConfig(String),
|
|
#[error("Connecting to database: {0}")]
|
|
ConnectingToDatabase(String),
|
|
#[error("Not found: {0}")]
|
|
NotFound(String),
|
|
#[error("Not authorized: {0}")]
|
|
NotAuthorized(String),
|
|
#[error("Metric not found: {0}")]
|
|
MetricNotFound(String),
|
|
#[error("Permission denied: {0}")]
|
|
PermissionDenied(String),
|
|
#[error("Require Admin privileges for {0}")]
|
|
RequireAdmin(String),
|
|
#[error("{0}")]
|
|
ExecutionErr(String),
|
|
#[error("IO error: {0}")]
|
|
IoErr(#[from] io::Error),
|
|
#[error("Sql error: {0}")]
|
|
SqlErr(#[from] sqlx::Error),
|
|
#[error("Bad request: {0}")]
|
|
BadRequest(String),
|
|
#[error("Quota exceeded: {0}")]
|
|
QuotaExceeded(String),
|
|
#[error("Internal: {0}")]
|
|
InternalErr(String),
|
|
#[error("Hexadecimal decoding error: {0}")]
|
|
HexErr(#[from] hex::FromHexError),
|
|
#[error("Migrating database: {0}")]
|
|
DatabaseMigration(#[from] MigrateError),
|
|
#[error("Non-zero exit status: {0}")]
|
|
ExitStatus(i32),
|
|
#[error("Err: {0:#}")]
|
|
Anyhow(#[from] anyhow::Error),
|
|
#[error("Error: {0:#?}")]
|
|
JsonErr(serde_json::Value),
|
|
#[error("{0}")]
|
|
AiError(String),
|
|
#[error("{0}")]
|
|
AlreadyCompleted(String),
|
|
}
|
|
|
|
impl Error {
|
|
/// https://docs.rs/anyhow/1/anyhow/struct.Error.html#display-representations
|
|
pub fn alt(&self) -> String {
|
|
format!("{:#}", self)
|
|
}
|
|
|
|
pub fn dbg(&self) -> String {
|
|
format!("{:?}", self)
|
|
}
|
|
}
|
|
|
|
pub fn to_anyhow<T: 'static + std::error::Error + Send + Sync>(e: T) -> anyhow::Error {
|
|
From::from(e)
|
|
}
|
|
|
|
impl IntoResponse for Error {
|
|
fn into_response(self) -> axum::response::Response {
|
|
let e = &self;
|
|
let body = Body::from(e.to_string());
|
|
|
|
let status = match self {
|
|
Self::NotFound(_) => axum::http::StatusCode::NOT_FOUND,
|
|
Self::NotAuthorized(_) => axum::http::StatusCode::UNAUTHORIZED,
|
|
Self::RequireAdmin(_) => axum::http::StatusCode::FORBIDDEN,
|
|
Self::SqlErr(_)
|
|
| Self::BadRequest(_)
|
|
| Self::AiError(_)
|
|
| Self::QuotaExceeded(_) => axum::http::StatusCode::BAD_REQUEST,
|
|
_ => axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
|
};
|
|
|
|
if matches!(status, axum::http::StatusCode::NOT_FOUND) {
|
|
tracing::warn!(message = e.to_string());
|
|
} else {
|
|
tracing::error!(message = e.to_string(), error = ?e);
|
|
};
|
|
|
|
axum::response::Response::builder()
|
|
.header("Content-Type", "text/plain")
|
|
.status(status)
|
|
.body(body)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
pub trait OrElseNotFound<T> {
|
|
fn or_else_not_found(self, s: impl ToString) -> Result<T>;
|
|
}
|
|
|
|
impl<T> OrElseNotFound<T> for Option<T> {
|
|
fn or_else_not_found(self, s: impl ToString) -> Result<T> {
|
|
self.ok_or_else(|| Error::NotFound(s.to_string()))
|
|
}
|
|
}
|
|
|
|
// Make our own error that wraps `anyhow::Error`.
|
|
pub struct AppError(anyhow::Error);
|
|
|
|
// Tell axum how to convert `AppError` into a response.
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
let body = Body::from(self.0.to_string());
|
|
tracing::error!(error = self.0.to_string());
|
|
axum::response::Response::builder()
|
|
.header("Content-Type", "text/plain")
|
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
.body(body)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
|
|
// `Result<_, AppError>`. That way you don't need to do that manually
|
|
impl<E> From<E> for AppError
|
|
where
|
|
E: Into<anyhow::Error>,
|
|
{
|
|
fn from(err: E) -> Self {
|
|
Self(err.into())
|
|
}
|
|
}
|