feat: Chat file metadata (#1133)
* chore: chat question metadata * chore: update * chore: update env * chore: update env
This commit is contained in:
parent
cd10010477
commit
5195dae3a5
|
|
@ -98,9 +98,10 @@ jobs:
|
|||
sed -i 's|GOTRUE_RATE_LIMIT_EMAIL_SENT=100|GOTRUE_RATE_LIMIT_EMAIL_SENT=1000|' .env
|
||||
sed -i 's|APPFLOWY_MAILER_SMTP_USERNAME=.*|APPFLOWY_MAILER_SMTP_USERNAME=${{ secrets.CI_GOTRUE_SMTP_USER }}|' .env
|
||||
sed -i 's|APPFLOWY_MAILER_SMTP_PASSWORD=.*|APPFLOWY_MAILER_SMTP_PASSWORD=${{ secrets.CI_GOTRUE_SMTP_PASS }}|' .env
|
||||
sed -i 's|APPFLOWY_AI_OPENAI_API_KEY=.*|APPFLOWY_AI_OPENAI_API_KEY=${{ secrets.CI_OPENAI_API_KEY }}|' .env
|
||||
sed -i "s|LOCAL_AI_AWS_ACCESS_KEY_ID=.*|LOCAL_AI_AWS_ACCESS_KEY_ID=${{ secrets.LOCAL_AI_AWS_ACCESS_KEY_ID }}|" .env
|
||||
sed -i "s|LOCAL_AI_AWS_SECRET_ACCESS_KEY=.*|LOCAL_AI_AWS_SECRET_ACCESS_KEY=${{ secrets.LOCAL_AI_AWS_SECRET_ACCESS_KEY }}|" .env
|
||||
sed -i 's|AI_OPENAI_API_KEY=.*|AI_OPENAI_API_KEY=${{ secrets.CI_OPENAI_API_KEY }}|' .env
|
||||
sed -i "s|AI_AWS_ACCESS_KEY_ID=.*|AI_AWS_ACCESS_KEY_ID=${{ secrets.LOCAL_AI_AWS_ACCESS_KEY_ID }}|" .env
|
||||
sed -i "s|AI_AWS_SECRET_ACCESS_KEY=.*|AI_AWS_SECRET_ACCESS_KEY=${{ secrets.LOCAL_AI_AWS_SECRET_ACCESS_KEY }}|" .env
|
||||
sed -i 's|AI_APPFLOWY_HOST=.*|AI_APPFLOWY_HOST=http://localhost:8000|' .env
|
||||
sed -i 's|APPFLOWY_WEB_URL=.*|APPFLOWY_WEB_URL=http://localhost:3000|' .env
|
||||
shell: bash
|
||||
|
||||
|
|
|
|||
23
deploy.env
23
deploy.env
|
|
@ -19,6 +19,9 @@ REDIS_PORT=6379
|
|||
MINIO_HOST=minio
|
||||
MINIO_PORT=9000
|
||||
|
||||
AWS_ACCESS_KEY=minioadmin
|
||||
AWS_SECRET=minioadmin
|
||||
|
||||
# AppFlowy Cloud
|
||||
## URL that connects to the gotrue docker container
|
||||
APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999
|
||||
|
|
@ -110,8 +113,8 @@ APPFLOWY_S3_CREATE_BUCKET=true
|
|||
# Keep this as true if you are using other S3 compatible storage provider other than AWS.
|
||||
APPFLOWY_S3_USE_MINIO=true
|
||||
APPFLOWY_S3_MINIO_URL=http://${MINIO_HOST}:${MINIO_PORT} # change this if you are using a different address for minio
|
||||
APPFLOWY_S3_ACCESS_KEY=minioadmin
|
||||
APPFLOWY_S3_SECRET_KEY=minioadmin
|
||||
APPFLOWY_S3_ACCESS_KEY=${AWS_ACCESS_KEY}
|
||||
APPFLOWY_S3_SECRET_KEY=${AWS_SECRET}
|
||||
APPFLOWY_S3_BUCKET=appflowy
|
||||
#APPFLOWY_S3_REGION=us-east-1
|
||||
|
||||
|
|
@ -146,12 +149,16 @@ NGINX_PORT=80
|
|||
NGINX_TLS_PORT=443
|
||||
|
||||
# AppFlowy AI
|
||||
APPFLOWY_AI_OPENAI_API_KEY=
|
||||
APPFLOWY_AI_SERVER_PORT=5001
|
||||
APPFLOWY_AI_SERVER_HOST=ai
|
||||
APPFLOWY_AI_DATABASE_URL=postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
APPFLOWY_AI_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
|
||||
APPFLOWY_LOCAL_AI_TEST_ENABLED=false
|
||||
AI_OPENAI_API_KEY=
|
||||
AI_SERVER_PORT=5001
|
||||
AI_SERVER_HOST=ai
|
||||
AI_DATABASE_URL=postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
AI_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
|
||||
LOCAL_AI_TEST_ENABLED=false
|
||||
AI_APPFLOWY_BUCKET_NAME=appflowy
|
||||
AI_APPFLOWY_HOST=http://your-host
|
||||
AI_AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY}
|
||||
AI_AWS_SECRET_ACCESS_KEY=${AWS_SECRET}
|
||||
|
||||
# AppFlowy Indexer
|
||||
APPFLOWY_INDEXER_ENABLED=true
|
||||
|
|
|
|||
24
dev.env
24
dev.env
|
|
@ -6,6 +6,10 @@ APPFLOWY_WEBSOCKET_MAILBOX_SIZE=6000
|
|||
APPFLOWY_DATABASE_MAX_CONNECTIONS=40
|
||||
APPFLOWY_DOCUMENT_CONTENT_SPLIT_LEN=8000
|
||||
|
||||
# AWS
|
||||
AWS_ACCESS_KEY=minioadmin
|
||||
AWS_SECRET=minioadmin
|
||||
|
||||
# This file is used to set the environment variables for local development
|
||||
# Copy this file to .env and change the values as needed
|
||||
|
||||
|
|
@ -83,8 +87,8 @@ GOTRUE_EXTERNAL_APPLE_REDIRECT_URI=http://localhost:9999/callback
|
|||
APPFLOWY_S3_CREATE_BUCKET=true
|
||||
APPFLOWY_S3_USE_MINIO=true
|
||||
APPFLOWY_S3_MINIO_URL=http://localhost:9000 # change this if you are using a different address for minio
|
||||
APPFLOWY_S3_ACCESS_KEY=minioadmin
|
||||
APPFLOWY_S3_SECRET_KEY=minioadmin
|
||||
APPFLOWY_S3_ACCESS_KEY=${AWS_ACCESS_KEY}
|
||||
APPFLOWY_S3_SECRET_KEY=${AWS_SECRET}
|
||||
APPFLOWY_S3_BUCKET=appflowy
|
||||
#APPFLOWY_S3_REGION=us-east-1
|
||||
|
||||
|
|
@ -113,12 +117,16 @@ GF_SECURITY_ADMIN_PASSWORD=password
|
|||
CLOUDFLARE_TUNNEL_TOKEN=
|
||||
|
||||
# AppFlowy AI
|
||||
APPFLOWY_AI_OPENAI_API_KEY=
|
||||
APPFLOWY_AI_SERVER_PORT=5001
|
||||
APPFLOWY_AI_SERVER_HOST=localhost
|
||||
APPFLOWY_AI_DATABASE_URL=postgresql+psycopg://postgres:password@localhost:5432/postgres
|
||||
APPFLOWY_AI_REDIS_URL=redis://redis:6379
|
||||
APPFLOWY_LOCAL_AI_TEST_ENABLED=false
|
||||
AI_OPENAI_API_KEY=
|
||||
AI_SERVER_PORT=5001
|
||||
AI_SERVER_HOST=localhost
|
||||
AI_DATABASE_URL=postgresql+psycopg://postgres:password@localhost:5432/postgres
|
||||
AI_REDIS_URL=redis://redis:6379
|
||||
LOCAL_AI_TEST_ENABLED=false
|
||||
AI_APPFLOWY_BUCKET_NAME=appflowy
|
||||
AI_APPFLOWY_HOST=http://localhost:8000
|
||||
AI_AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY}
|
||||
AI_AWS_SECRET_ACCESS_KEY=${AWS_SECRET}
|
||||
|
||||
# AppFlowy Indexer
|
||||
APPFLOWY_INDEXER_ENABLED=true
|
||||
|
|
|
|||
|
|
@ -131,15 +131,15 @@ services:
|
|||
- APPFLOWY_ACCESS_CONTROL=${APPFLOWY_ACCESS_CONTROL}
|
||||
# For the CI testing, we set the database connection to 20. The default value is 40.
|
||||
- APPFLOWY_DATABASE_MAX_CONNECTIONS=20
|
||||
- APPFLOWY_AI_SERVER_HOST=${APPFLOWY_AI_SERVER_HOST}
|
||||
- APPFLOWY_AI_SERVER_PORT=${APPFLOWY_AI_SERVER_PORT}
|
||||
- AI_SERVER_HOST=${AI_SERVER_HOST}
|
||||
- AI_SERVER_PORT=${AI_SERVER_PORT}
|
||||
- APPFLOWY_WEB_URL=${APPFLOWY_WEB_URL}
|
||||
- APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST}
|
||||
- APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT}
|
||||
- APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME}
|
||||
- APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL}
|
||||
- APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD}
|
||||
- APPFLOWY_AI_OPENAI_API_KEY=${APPFLOWY_AI_OPENAI_API_KEY}
|
||||
- AI_OPENAI_API_KEY=${AI_OPENAI_API_KEY}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
|
@ -171,12 +171,14 @@ services:
|
|||
ports:
|
||||
- "5001:5001"
|
||||
environment:
|
||||
- OPENAI_API_KEY=${APPFLOWY_AI_OPENAI_API_KEY}
|
||||
- LOCAL_AI_AWS_ACCESS_KEY_ID=${LOCAL_AI_AWS_ACCESS_KEY_ID}
|
||||
- LOCAL_AI_AWS_SECRET_ACCESS_KEY=${LOCAL_AI_AWS_SECRET_ACCESS_KEY}
|
||||
- APPFLOWY_AI_SERVER_PORT=${APPFLOWY_AI_SERVER_PORT}
|
||||
- APPFLOWY_AI_DATABASE_URL=${APPFLOWY_AI_DATABASE_URL}
|
||||
- APPFLOWY_AI_REDIS_URL=${APPFLOWY_AI_REDIS_URL}
|
||||
- OPENAI_API_KEY=${AI_OPENAI_API_KEY}
|
||||
- AI_AWS_ACCESS_KEY_ID=${AI_AWS_ACCESS_KEY_ID}
|
||||
- AI_AWS_SECRET_ACCESS_KEY=${AI_AWS_SECRET_ACCESS_KEY}
|
||||
- AI_SERVER_PORT=${AI_SERVER_PORT}
|
||||
- AI_DATABASE_URL=${AI_DATABASE_URL}
|
||||
- AI_REDIS_URL=${AI_REDIS_URL}
|
||||
- AI_APPFLOWY_BUCKET_NAME=${AI_APPFLOWY_BUCKET_NAME}
|
||||
- AI_APPFLOWY_HOST=${AI_APPFLOWY_HOST}
|
||||
|
||||
appflowy_worker:
|
||||
restart: on-failure
|
||||
|
|
|
|||
|
|
@ -133,11 +133,10 @@ services:
|
|||
- APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND}
|
||||
- APPFLOWY_ACCESS_CONTROL=${APPFLOWY_ACCESS_CONTROL}
|
||||
- APPFLOWY_DATABASE_MAX_CONNECTIONS=${APPFLOWY_DATABASE_MAX_CONNECTIONS}
|
||||
- APPFLOWY_AI_SERVER_HOST=${APPFLOWY_AI_SERVER_HOST}
|
||||
- APPFLOWY_AI_SERVER_PORT=${APPFLOWY_AI_SERVER_PORT}
|
||||
- APPFLOWY_AI_OPENAI_API_KEY=${APPFLOWY_AI_OPENAI_API_KEY}
|
||||
- AI_SERVER_HOST=${AI_SERVER_HOST}
|
||||
- AI_SERVER_PORT=${AI_SERVER_PORT}
|
||||
- AI_OPENAI_API_KEY=${AI_OPENAI_API_KEY}
|
||||
# Uncomment this line if AppFlowy Web has been deployed
|
||||
# - APPFLOWY_WEB_URL=${APPFLOWY_WEB_URL}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
|
@ -161,10 +160,10 @@ services:
|
|||
restart: on-failure
|
||||
image: appflowyinc/appflowy_ai:${APPFLOWY_AI_VERSION:-latest}
|
||||
environment:
|
||||
- OPENAI_API_KEY=${APPFLOWY_AI_OPENAI_API_KEY}
|
||||
- APPFLOWY_AI_SERVER_PORT=${APPFLOWY_AI_SERVER_PORT}
|
||||
- APPFLOWY_AI_DATABASE_URL=${APPFLOWY_AI_DATABASE_URL}
|
||||
- APPFLOWY_AI_REDIS_URL=${APPFLOWY_AI_REDIS_URL}
|
||||
- OPENAI_API_KEY=${AI_OPENAI_API_KEY}
|
||||
- APPFLOWY_AI_SERVER_PORT=${AI_SERVER_PORT}
|
||||
- APPFLOWY_AI_DATABASE_URL=${AI_DATABASE_URL}
|
||||
- APPFLOWY_AI_REDIS_URL=${AI_REDIS_URL}
|
||||
|
||||
appflowy_worker:
|
||||
restart: on-failure
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use crate::dto::{
|
||||
AIModel, CalculateSimilarityParams, ChatAnswer, ChatQuestion, CompleteTextResponse,
|
||||
CompletionType, CreateChatContext, CustomPrompt, Document, LocalAIConfig, MessageData,
|
||||
RepeatedLocalAIPackage, RepeatedRelatedQuestion, ResponseFormat, SearchDocumentsRequest,
|
||||
SimilarityResponse, SummarizeRowResponse, TranslateRowData, TranslateRowResponse,
|
||||
QuestionMetadata, RepeatedLocalAIPackage, RepeatedRelatedQuestion, ResponseFormat,
|
||||
SearchDocumentsRequest, SimilarityResponse, SummarizeRowResponse, TranslateRowData,
|
||||
TranslateRowResponse,
|
||||
};
|
||||
use crate::error::AIError;
|
||||
|
||||
|
|
@ -173,6 +174,7 @@ impl AppFlowyAIClient {
|
|||
|
||||
pub async fn send_question(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
question_id: i64,
|
||||
content: &str,
|
||||
|
|
@ -184,10 +186,13 @@ impl AppFlowyAIClient {
|
|||
data: MessageData {
|
||||
content: content.to_string(),
|
||||
metadata,
|
||||
rag_ids: vec![],
|
||||
message_id: Some(question_id.to_string()),
|
||||
},
|
||||
format: Default::default(),
|
||||
metadata: QuestionMetadata {
|
||||
workspace_id: workspace_id.to_string(),
|
||||
rag_ids: vec![],
|
||||
},
|
||||
};
|
||||
let url = format!("{}/chat/message", self.url);
|
||||
let resp = self
|
||||
|
|
@ -203,6 +208,7 @@ impl AppFlowyAIClient {
|
|||
|
||||
pub async fn stream_question(
|
||||
&self,
|
||||
workspace_id: String,
|
||||
chat_id: &str,
|
||||
content: &str,
|
||||
metadata: Option<Value>,
|
||||
|
|
@ -214,10 +220,13 @@ impl AppFlowyAIClient {
|
|||
data: MessageData {
|
||||
content: content.to_string(),
|
||||
metadata,
|
||||
rag_ids,
|
||||
message_id: None,
|
||||
},
|
||||
format: Default::default(),
|
||||
metadata: QuestionMetadata {
|
||||
workspace_id,
|
||||
rag_ids,
|
||||
},
|
||||
};
|
||||
let url = format!("{}/chat/message/stream", self.url);
|
||||
let resp = self
|
||||
|
|
@ -230,8 +239,10 @@ impl AppFlowyAIClient {
|
|||
AIResponse::<()>::stream_response(resp).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn stream_question_v2(
|
||||
&self,
|
||||
workspace_id: String,
|
||||
chat_id: &str,
|
||||
question_id: i64,
|
||||
content: &str,
|
||||
|
|
@ -244,10 +255,13 @@ impl AppFlowyAIClient {
|
|||
data: MessageData {
|
||||
content: content.to_string(),
|
||||
metadata,
|
||||
rag_ids,
|
||||
message_id: Some(question_id.to_string()),
|
||||
},
|
||||
format: ResponseFormat::default(),
|
||||
metadata: QuestionMetadata {
|
||||
workspace_id,
|
||||
rag_ids,
|
||||
},
|
||||
};
|
||||
self.stream_question_v3(model, json).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@ pub struct ChatQuestion {
|
|||
pub data: MessageData,
|
||||
#[serde(default)]
|
||||
pub format: ResponseFormat,
|
||||
pub metadata: QuestionMetadata,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct QuestionMetadata {
|
||||
pub workspace_id: String,
|
||||
pub rag_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
|
|
@ -60,7 +67,7 @@ pub struct OutputContentMetadata {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub custom_image_prompt: Option<String>,
|
||||
|
||||
/// The image model to use for generation (default: "dall-e-2").
|
||||
/// The image model to use for generation (default: "dall-e-3").
|
||||
#[serde(default = "default_image_model")]
|
||||
pub image_model: String,
|
||||
|
||||
|
|
@ -81,7 +88,7 @@ pub struct OutputContentMetadata {
|
|||
|
||||
// Default values for the fields
|
||||
fn default_image_model() -> String {
|
||||
"dall-e-2".to_string()
|
||||
"dall-e-3".to_string()
|
||||
}
|
||||
|
||||
fn default_image_size() -> Option<String> {
|
||||
|
|
@ -98,8 +105,6 @@ pub struct MessageData {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub rag_ids: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub message_id: Option<String>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,14 @@ async fn create_chat_context_test() {
|
|||
};
|
||||
client.create_chat_text_context(context).await.unwrap();
|
||||
let resp = client
|
||||
.send_question(&chat_id, 1, "Where I live?", &AIModel::GPT4oMini, None)
|
||||
.send_question(
|
||||
&uuid::Uuid::new_v4().to_string(),
|
||||
&chat_id,
|
||||
1,
|
||||
"Where I live?",
|
||||
&AIModel::GPT4oMini,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
// response will be something like:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
use crate::appflowy_ai_client;
|
||||
|
||||
use appflowy_ai_client::dto::{AIModel, STREAM_ANSWER_KEY};
|
||||
use appflowy_ai_client::error::AIError;
|
||||
use futures::stream::StreamExt;
|
||||
use infra::reqwest::JsonStream;
|
||||
use appflowy_ai_client::dto::AIModel;
|
||||
|
||||
#[tokio::test]
|
||||
async fn qa_test() {
|
||||
|
|
@ -11,7 +8,14 @@ async fn qa_test() {
|
|||
client.health_check().await.unwrap();
|
||||
let chat_id = uuid::Uuid::new_v4().to_string();
|
||||
let resp = client
|
||||
.send_question(&chat_id, 1, "I feel hungry", &AIModel::GPT4o, None)
|
||||
.send_question(
|
||||
&uuid::Uuid::new_v4().to_string(),
|
||||
&chat_id,
|
||||
1,
|
||||
"I feel hungry",
|
||||
&AIModel::GPT4o,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!resp.content.is_empty());
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ pub fn ai_test_enabled() -> bool {
|
|||
load_env();
|
||||
|
||||
// local ai test is disable by default
|
||||
let enabled = get_bool_from_env_var("APPFLOWY_LOCAL_AI_TEST_ENABLED");
|
||||
let enabled = get_bool_from_env_var("LOCAL_AI_TEST_ENABLED");
|
||||
if enabled {
|
||||
trace!("Local AI test is enabled");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ pub trait BucketClient {
|
|||
pub trait BlobKey: Send + Sync {
|
||||
fn workspace_id(&self) -> &Uuid;
|
||||
fn object_key(&self) -> String;
|
||||
fn meta_key(&self) -> String;
|
||||
fn blob_metadata_key(&self) -> String;
|
||||
fn e_tag(&self) -> &str;
|
||||
}
|
||||
|
||||
|
|
@ -99,11 +99,11 @@ where
|
|||
file_type: String,
|
||||
file_size: usize,
|
||||
) -> Result<(), AppError> {
|
||||
if is_blob_metadata_exists(&self.pg_pool, key.workspace_id(), &key.meta_key()).await? {
|
||||
if is_blob_metadata_exists(&self.pg_pool, key.workspace_id(), &key.blob_metadata_key()).await? {
|
||||
warn!(
|
||||
"file already exists, workspace_id: {}, meta_key: {}",
|
||||
"file already exists, workspace_id: {}, blob_metadata_key: {}",
|
||||
key.workspace_id(),
|
||||
key.meta_key()
|
||||
key.blob_metadata_key()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ where
|
|||
.await?;
|
||||
insert_blob_metadata(
|
||||
&self.pg_pool,
|
||||
&key.meta_key(),
|
||||
&key.blob_metadata_key(),
|
||||
key.workspace_id(),
|
||||
&file_type,
|
||||
file_size,
|
||||
|
|
@ -127,7 +127,7 @@ where
|
|||
self.client.delete_blob(&key.object_key()).await?;
|
||||
|
||||
let mut tx = self.pg_pool.begin().await?;
|
||||
delete_blob_metadata(&mut tx, key.workspace_id(), &key.meta_key()).await?;
|
||||
delete_blob_metadata(&mut tx, key.workspace_id(), &key.blob_metadata_key()).await?;
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -135,9 +135,9 @@ where
|
|||
pub async fn get_blob_metadata(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
meta_key: &str,
|
||||
store_key: &str,
|
||||
) -> Result<AFBlobMetadataRow, AppError> {
|
||||
let metadata = get_blob_metadata(&self.pg_pool, workspace_id, meta_key).await?;
|
||||
let metadata = get_blob_metadata(&self.pg_pool, workspace_id, store_key).await?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ where
|
|||
self.client.complete_upload(&key.object_key(), req).await?;
|
||||
insert_blob_metadata(
|
||||
&self.pg_pool,
|
||||
&key.meta_key(),
|
||||
&key.blob_metadata_key(),
|
||||
key.workspace_id(),
|
||||
&content_type,
|
||||
content_length,
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ pub async fn init_state(config: &Config, rt_cmd_tx: CLCommandSender) -> Result<A
|
|||
enable: get_env_var("APPFLOWY_INDEXER_ENABLED", "true")
|
||||
.parse::<bool>()
|
||||
.unwrap_or(true),
|
||||
openai_api_key: Secret::new(get_env_var("APPFLOWY_AI_OPENAI_API_KEY", "")),
|
||||
openai_api_key: Secret::new(get_env_var("AI_OPENAI_API_KEY", "")),
|
||||
embedding_buffer_size: get_env_var("APPFLOWY_INDEXER_EMBEDDING_BUFFER_SIZE", "2000")
|
||||
.parse::<usize>()
|
||||
.unwrap_or(2000),
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ pub fn get_configuration() -> Result<Config, anyhow::Error> {
|
|||
redis_worker_count: get_env_var("APPFLOWY_REDIS_WORKERS", "60").parse()?,
|
||||
ai: AISettings {
|
||||
port: get_env_var("APPFLOWY_AI_SERVER_PORT", "5001").parse()?,
|
||||
host: get_env_var("APPFLOWY_AI_SERVER_HOST", "localhost"),
|
||||
host: get_env_var("AI_SERVER_HOST", "localhost"),
|
||||
},
|
||||
};
|
||||
Ok(config)
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ pub async fn create_app(listener: TcpListener, config: Config) -> Result<(), Err
|
|||
.parse::<bool>()
|
||||
.unwrap_or(true),
|
||||
open_api_key: Secret::new(appflowy_collaborate::config::get_env_var(
|
||||
"APPFLOWY_AI_OPENAI_API_KEY",
|
||||
"AI_OPENAI_API_KEY",
|
||||
"",
|
||||
)),
|
||||
tick_interval_secs: 10,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ use serde::Deserialize;
|
|||
use crate::api::util::ai_model_from_header;
|
||||
use app_error::AppError;
|
||||
use appflowy_ai_client::dto::{
|
||||
ChatQuestion, ChatQuestionQuery, CreateChatContext, MessageData, RepeatedRelatedQuestion,
|
||||
ChatQuestion, ChatQuestionQuery, CreateChatContext, MessageData, QuestionMetadata,
|
||||
RepeatedRelatedQuestion,
|
||||
};
|
||||
use authentication::jwt::UserUuid;
|
||||
use bytes::Bytes;
|
||||
|
|
@ -88,7 +89,7 @@ pub fn chat_scope() -> Scope {
|
|||
)
|
||||
.service(
|
||||
web::resource("/{chat_id}/{message_id}/v2/answer/stream")
|
||||
.route(web::get().to(answer_stream_v2_handler))
|
||||
.route(web::get().to(answer_stream_v2_handler)) // Deprecated since 0.9.2
|
||||
)
|
||||
.service(
|
||||
web::resource("/{chat_id}/answer/stream")
|
||||
|
|
@ -140,13 +141,22 @@ async fn create_chat_context_handler(
|
|||
}
|
||||
|
||||
async fn update_question_handler(
|
||||
path: web::Path<(String, String)>,
|
||||
state: Data<AppState>,
|
||||
payload: Json<UpdateChatMessageContentParams>,
|
||||
req: HttpRequest,
|
||||
) -> actix_web::Result<JsonAppResponse<()>> {
|
||||
let (workspace_id, _chat_id) = path.into_inner();
|
||||
let params = payload.into_inner();
|
||||
let ai_model = ai_model_from_header(&req);
|
||||
update_chat_message(&state.pg_pool, params, state.ai_client.clone(), ai_model).await?;
|
||||
update_chat_message(
|
||||
workspace_id,
|
||||
&state.pg_pool,
|
||||
params,
|
||||
state.ai_client.clone(),
|
||||
ai_model,
|
||||
)
|
||||
.await?;
|
||||
Ok(AppResponse::Ok().into())
|
||||
}
|
||||
|
||||
|
|
@ -236,9 +246,10 @@ async fn answer_handler(
|
|||
state: Data<AppState>,
|
||||
req: HttpRequest,
|
||||
) -> actix_web::Result<JsonAppResponse<ChatMessage>> {
|
||||
let (_workspace_id, chat_id, message_id) = path.into_inner();
|
||||
let (workspace_id, chat_id, message_id) = path.into_inner();
|
||||
let ai_model = ai_model_from_header(&req);
|
||||
let message = generate_chat_message_answer(
|
||||
workspace_id,
|
||||
&state.pg_pool,
|
||||
state.ai_client.clone(),
|
||||
message_id,
|
||||
|
|
@ -255,14 +266,21 @@ async fn answer_stream_handler(
|
|||
state: Data<AppState>,
|
||||
req: HttpRequest,
|
||||
) -> actix_web::Result<HttpResponse> {
|
||||
let (_workspace_id, chat_id, question_id) = path.into_inner();
|
||||
let (workspace_id, chat_id, question_id) = path.into_inner();
|
||||
let (content, metadata) =
|
||||
chat::chat_ops::select_chat_message_content(&state.pg_pool, question_id).await?;
|
||||
let rag_ids = chat::chat_ops::select_chat_rag_ids(&state.pg_pool, &chat_id).await?;
|
||||
let ai_model = ai_model_from_header(&req);
|
||||
match state
|
||||
.ai_client
|
||||
.stream_question(&chat_id, &content, Some(metadata), rag_ids, &ai_model)
|
||||
.stream_question(
|
||||
workspace_id,
|
||||
&chat_id,
|
||||
&content,
|
||||
Some(metadata),
|
||||
rag_ids,
|
||||
&ai_model,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(answer_stream) => {
|
||||
|
|
@ -289,7 +307,7 @@ async fn answer_stream_v2_handler(
|
|||
state: Data<AppState>,
|
||||
req: HttpRequest,
|
||||
) -> actix_web::Result<HttpResponse> {
|
||||
let (_workspace_id, chat_id, question_id) = path.into_inner();
|
||||
let (workspace_id, chat_id, question_id) = path.into_inner();
|
||||
let (content, metadata) =
|
||||
chat::chat_ops::select_chat_message_content(&state.pg_pool, question_id).await?;
|
||||
let rag_ids = chat::chat_ops::select_chat_rag_ids(&state.pg_pool, &chat_id).await?;
|
||||
|
|
@ -304,6 +322,7 @@ async fn answer_stream_v2_handler(
|
|||
match state
|
||||
.ai_client
|
||||
.stream_question_v2(
|
||||
workspace_id,
|
||||
&chat_id,
|
||||
question_id,
|
||||
&content,
|
||||
|
|
@ -333,10 +352,12 @@ async fn answer_stream_v2_handler(
|
|||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
async fn answer_stream_v3_handler(
|
||||
path: web::Path<(String, String)>,
|
||||
payload: Json<ChatQuestionQuery>,
|
||||
state: Data<AppState>,
|
||||
req: HttpRequest,
|
||||
) -> actix_web::Result<HttpResponse> {
|
||||
let (workspace_id, _) = path.into_inner();
|
||||
let payload = payload.into_inner();
|
||||
let (content, metadata) =
|
||||
chat::chat_ops::select_chat_message_content(&state.pg_pool, payload.question_id).await?;
|
||||
|
|
@ -348,10 +369,13 @@ async fn answer_stream_v3_handler(
|
|||
data: MessageData {
|
||||
content: content.to_string(),
|
||||
metadata: Some(metadata),
|
||||
rag_ids,
|
||||
message_id: Some(payload.question_id.to_string()),
|
||||
},
|
||||
format: payload.format,
|
||||
metadata: QuestionMetadata {
|
||||
workspace_id,
|
||||
rag_ids,
|
||||
},
|
||||
};
|
||||
trace!("[Chat] stream v3 {:?}", question);
|
||||
match state
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ async fn get_blob_by_object_key(
|
|||
// Get the metadata
|
||||
let result = state
|
||||
.bucket_storage
|
||||
.get_blob_metadata(key.workspace_id(), &key.meta_key())
|
||||
.get_blob_metadata(key.workspace_id(), &key.blob_metadata_key())
|
||||
.await;
|
||||
|
||||
if let Err(err) = result.as_ref() {
|
||||
|
|
@ -424,7 +424,7 @@ async fn get_blob_metadata_handler(
|
|||
// Get the metadata
|
||||
let metadata = state
|
||||
.bucket_storage
|
||||
.get_blob_metadata(&path.workspace_id, &path.meta_key())
|
||||
.get_blob_metadata(&path.workspace_id, &path.blob_metadata_key())
|
||||
.await
|
||||
.map(|meta| BlobMetadata {
|
||||
workspace_id: meta.workspace_id,
|
||||
|
|
@ -448,7 +448,7 @@ async fn get_blob_metadata_v1_handler(
|
|||
// Get the metadata
|
||||
let metadata = state
|
||||
.bucket_storage
|
||||
.get_blob_metadata(&path.workspace_id, &path.meta_key())
|
||||
.get_blob_metadata(&path.workspace_id, &path.blob_metadata_key())
|
||||
.await
|
||||
.map(|meta| BlobMetadata {
|
||||
workspace_id: meta.workspace_id,
|
||||
|
|
@ -589,7 +589,7 @@ impl BlobKey for BlobPathV0 {
|
|||
format!("{}/{}", self.workspace_id, self.file_id)
|
||||
}
|
||||
|
||||
fn meta_key(&self) -> String {
|
||||
fn blob_metadata_key(&self) -> String {
|
||||
self.file_id.clone()
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +615,7 @@ impl BlobKey for BlobPathV1 {
|
|||
format!("{}/{}/{}", self.workspace_id, self.parent_dir, self.file_id)
|
||||
}
|
||||
|
||||
fn meta_key(&self) -> String {
|
||||
fn blob_metadata_key(&self) -> String {
|
||||
format!("{}_{}", self.parent_dir, self.file_id)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ pub async fn init_state(config: &Config, rt_cmd_tx: CLCommandSender) -> Result<A
|
|||
enable: get_env_var("APPFLOWY_INDEXER_ENABLED", "true")
|
||||
.parse::<bool>()
|
||||
.unwrap_or(true),
|
||||
openai_api_key: Secret::new(get_env_var("APPFLOWY_AI_OPENAI_API_KEY", "")),
|
||||
openai_api_key: Secret::new(get_env_var("AI_OPENAI_API_KEY", "")),
|
||||
embedding_buffer_size: appflowy_collaborate::config::get_env_var(
|
||||
"APPFLOWY_INDEXER_EMBEDDING_BUFFER_SIZE",
|
||||
"5000",
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ pub(crate) async fn delete_chat(pg_pool: &PgPool, chat_id: &str) -> Result<(), A
|
|||
}
|
||||
|
||||
pub async fn update_chat_message(
|
||||
workspace_id: String,
|
||||
pg_pool: &PgPool,
|
||||
params: UpdateChatMessageContentParams,
|
||||
ai_client: AppFlowyAIClient,
|
||||
|
|
@ -60,6 +61,7 @@ pub async fn update_chat_message(
|
|||
// TODO(nathan): query the metadata from the database
|
||||
let new_answer = ai_client
|
||||
.send_question(
|
||||
&workspace_id,
|
||||
¶ms.chat_id,
|
||||
params.message_id,
|
||||
¶ms.content,
|
||||
|
|
@ -81,6 +83,7 @@ pub async fn update_chat_message(
|
|||
}
|
||||
|
||||
pub async fn generate_chat_message_answer(
|
||||
workspace_id: String,
|
||||
pg_pool: &PgPool,
|
||||
ai_client: AppFlowyAIClient,
|
||||
question_message_id: i64,
|
||||
|
|
@ -91,6 +94,7 @@ pub async fn generate_chat_message_answer(
|
|||
chat::chat_ops::select_chat_message_content(pg_pool, question_message_id).await?;
|
||||
let new_answer = ai_client
|
||||
.send_question(
|
||||
&workspace_id,
|
||||
chat_id,
|
||||
question_message_id,
|
||||
&content,
|
||||
|
|
@ -145,6 +149,7 @@ pub async fn create_chat_message(
|
|||
pub async fn create_chat_message_stream(
|
||||
pg_pool: &PgPool,
|
||||
uid: i64,
|
||||
workspace_id: String,
|
||||
chat_id: String,
|
||||
params: CreateChatMessageParams,
|
||||
ai_client: AppFlowyAIClient,
|
||||
|
|
@ -186,7 +191,7 @@ pub async fn create_chat_message_stream(
|
|||
match params.message_type {
|
||||
ChatMessageType::System => {}
|
||||
ChatMessageType::User => {
|
||||
let answer = match ai_client.send_question(&chat_id,question_id, ¶ms.content, &ai_model, Some(json!(params.metadata))).await {
|
||||
let answer = match ai_client.send_question(&workspace_id, &chat_id,question_id, ¶ms.content, &ai_model, Some(json!(params.metadata))).await {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
error!("Failed to send question to AI: {}", err);
|
||||
|
|
|
|||
|
|
@ -241,8 +241,8 @@ pub fn get_configuration() -> Result<Config, anyhow::Error> {
|
|||
region: get_env_var("APPFLOWY_S3_REGION", ""),
|
||||
},
|
||||
appflowy_ai: AppFlowyAISetting {
|
||||
port: get_env_var("APPFLOWY_AI_SERVER_PORT", "5001").into(),
|
||||
host: get_env_var("APPFLOWY_AI_SERVER_HOST", "localhost").into(),
|
||||
port: get_env_var("AI_SERVER_PORT", "5001").into(),
|
||||
host: get_env_var("AI_SERVER_HOST", "localhost").into(),
|
||||
},
|
||||
grpc_history: GrpcHistorySetting {
|
||||
addrs: get_env_var("APPFLOWY_GRPC_HISTORY_ADDRS", "http://localhost:50051"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue