mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 12:06:35 +08:00
feat(server): refactor for byok (#14911)
This commit is contained in:
@@ -686,17 +686,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Apply Updates",
|
||||
"action": "Apply Updates",
|
||||
"model": "claude-sonnet-4-5@20250929",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"template": "\nYou are a Markdown document update engine.\n\nYou will be given:\n\n1. content: The original Markdown document\n - The content is structured into blocks.\n - Each block starts with a comment like <!-- block_id=... flavour=... --> and contains the block's content.\n - The content is {{content}}\n\n2. op: A description of the edit intention\n - This describes the semantic meaning of the edit, such as \"Bold the first paragraph\".\n - The op is {{op}}\n\n3. updates: A Markdown snippet\n - The updates is {{updates}}\n - This represents the block-level changes to apply to the original Markdown.\n - The update may:\n - **Replace** an existing block (same block_id, new content)\n - **Delete** block(s) using <!-- delete block BLOCK_ID -->\n - **Insert** new block(s) with a new unique block_id\n - When performing deletions, the update will include **surrounding context blocks** (or use <!-- existing blocks -->) to help you determine where and what to delete.\n\nYour task:\n- Apply the update in <updates> to the document in <code>, following the intent described in <op>.\n- Preserve all block_id and flavour comments.\n- Maintain the original block order unless the update clearly appends new blocks.\n- Do not remove or alter unrelated blocks.\n- Output only the fully updated Markdown content. Do not wrap the content in ```markdown.\n\n---\n\n✍️ Examples\n\n✅ Replacement (modifying an existing block)\n\n<code>\n<!-- block_id=101 flavour=paragraph -->\n## Introduction\n\n<!-- block_id=102 flavour=paragraph -->\nThis document provides an overview of the system architecture and its components.\n</code>\n\n<op>\nMake the introduction more formal.\n</op>\n\n<updates>\n<!-- block_id=102 flavour=paragraph -->\nThis document outlines the architectural design and individual components of the system in detail.\n</updates>\n\nExpected Output:\n<!-- block_id=101 flavour=paragraph -->\n## Introduction\n\n<!-- block_id=102 flavour=paragraph -->\nThis document outlines the architectural design and individual components of the system in detail.\n\n---\n\n➕ Insertion (adding new content)\n\n<code>\n<!-- block_id=201 flavour=paragraph -->\n# Project Summary\n\n<!-- block_id=202 flavour=paragraph -->\nThis project aims to build a collaborative text editing tool.\n</code>\n\n<op>\nAdd a disclaimer section at the end.\n</op>\n\n<updates>\n<!-- block_id=new-301 flavour=paragraph -->\n## Disclaimer\n\n<!-- block_id=new-302 flavour=paragraph -->\nThis document is subject to change. Do not distribute externally.\n</updates>\n\nExpected Output:\n<!-- block_id=201 flavour=paragraph -->\n# Project Summary\n\n<!-- block_id=202 flavour=paragraph -->\nThis project aims to build a collaborative text editing tool.\n\n<!-- block_id=new-301 flavour=paragraph -->\n## Disclaimer\n\n<!-- block_id=new-302 flavour=paragraph -->\nThis document is subject to change. Do not distribute externally.\n\n---\n\n❌ Deletion (removing blocks)\n\n<code>\n<!-- block_id=401 flavour=paragraph -->\n## Author\n\n<!-- block_id=402 flavour=paragraph -->\nWritten by the AI team at OpenResearch.\n\n<!-- block_id=403 flavour=paragraph -->\n## Experimental Section\n\n<!-- block_id=404 flavour=paragraph -->\nThe following section is still under development and may change without notice.\n\n<!-- block_id=405 flavour=paragraph -->\n## License\n\n<!-- block_id=406 flavour=paragraph -->\nThis document is licensed under CC BY-NC 4.0.\n</code>\n\n<op>\nRemove the experimental section.\n</op>\n\n<updates>\n<!-- delete block_id=403 -->\n<!-- delete block_id=404 -->\n</updates>\n\nExpected Output:\n<!-- block_id=401 flavour=paragraph -->\n## Author\n\n<!-- block_id=402 flavour=paragraph -->\nWritten by the AI team at OpenResearch.\n\n<!-- block_id=405 flavour=paragraph -->\n## License\n\n<!-- block_id=406 flavour=paragraph -->\nThis document is licensed under CC BY-NC 4.0.\n\n---\n\nNow apply the `updates` to the `content`, following the intent in `op`, and return the updated Markdown.\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Code Artifact",
|
||||
"model": "claude-sonnet-4-5@20250929",
|
||||
|
||||
@@ -319,7 +319,7 @@ pub struct RequestedModelMatchResponse {
|
||||
pub struct ModelRegistryResolveRequest {
|
||||
#[napi(
|
||||
ts_type = "'openai_chat' | 'openai_responses' | 'anthropic' | 'cloudflare_workers_ai' | 'gemini_api' | \
|
||||
'gemini_vertex' | 'fal' | 'perplexity' | 'anthropic_vertex' | 'morph'"
|
||||
'gemini_vertex' | 'fal' | 'anthropic_vertex'"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub backend_kind: Option<String>,
|
||||
@@ -333,7 +333,7 @@ pub struct ModelRegistryResolveRequest {
|
||||
pub struct ModelRegistryMatchRequest {
|
||||
#[napi(
|
||||
ts_type = "'openai_chat' | 'openai_responses' | 'anthropic' | 'cloudflare_workers_ai' | 'gemini_api' | \
|
||||
'gemini_vertex' | 'fal' | 'perplexity' | 'anthropic_vertex' | 'morph'"
|
||||
'gemini_vertex' | 'fal' | 'anthropic_vertex'"
|
||||
)]
|
||||
pub backend_kind: String,
|
||||
pub cond: ModelConditionsContract,
|
||||
@@ -346,7 +346,7 @@ pub struct ModelRegistryMatchRequest {
|
||||
pub struct ModelRegistryVariantContract {
|
||||
#[napi(
|
||||
ts_type = "'openai_chat' | 'openai_responses' | 'anthropic' | 'cloudflare_workers_ai' | 'gemini_api' | \
|
||||
'gemini_vertex' | 'fal' | 'perplexity' | 'anthropic_vertex' | 'morph'"
|
||||
'gemini_vertex' | 'fal' | 'anthropic_vertex'"
|
||||
)]
|
||||
pub backend_kind: String,
|
||||
pub canonical_key: String,
|
||||
|
||||
@@ -7,7 +7,7 @@ pub(crate) use error::{
|
||||
STREAM_ABORTED_REASON, STREAM_CALLBACK_DISPATCH_FAILED_REASON, STREAM_END_MARKER, callback_dispatch_failed_reason,
|
||||
invalid_arg,
|
||||
};
|
||||
pub(crate) use stream::emit_error_event;
|
||||
pub(crate) use stream::{emit_error_event, emit_provider_selected_event};
|
||||
pub use stream::{
|
||||
llm_dispatch_prepared_stream, llm_dispatch_tool_loop_stream, llm_dispatch_tool_loop_stream_prepared,
|
||||
llm_dispatch_tool_loop_stream_routed,
|
||||
|
||||
@@ -106,14 +106,18 @@ fn spawn_prepared_stream(
|
||||
if reason.starts_with(STREAM_CALLBACK_DISPATCH_FAILED_REASON)
|
||||
);
|
||||
|
||||
if let Err(error) = result
|
||||
if let Err(error) = &result
|
||||
&& !aborted_in_worker.load(Ordering::Relaxed)
|
||||
&& !callback_dispatch_failed
|
||||
&& !is_abort_error(&error)
|
||||
&& !is_abort_error(error)
|
||||
{
|
||||
emit_error_event(&callback, error.to_string(), "dispatch_error");
|
||||
}
|
||||
|
||||
if let Ok(provider_id) = result {
|
||||
emit_provider_selected_event(&callback, provider_id);
|
||||
}
|
||||
|
||||
if !callback_dispatch_failed {
|
||||
let _ = callback.call(
|
||||
Ok(STREAM_END_MARKER.to_string()),
|
||||
@@ -129,7 +133,7 @@ fn dispatch_prepared_stream_with_fallback(
|
||||
routes: &[PreparedDispatchRoute],
|
||||
callback: &ThreadsafeFunction<String, ()>,
|
||||
aborted: &AtomicBool,
|
||||
) -> std::result::Result<(), BackendError> {
|
||||
) -> std::result::Result<String, BackendError> {
|
||||
dispatch_prepared_stream_with_fallback_using_client(&DefaultHttpClient::default(), routes, aborted, |event| {
|
||||
emit_stream_event(callback, event)
|
||||
})
|
||||
@@ -140,7 +144,7 @@ fn dispatch_prepared_stream_with_fallback_using_client<F>(
|
||||
routes: &[PreparedDispatchRoute],
|
||||
aborted: &AtomicBool,
|
||||
mut emit_event: F,
|
||||
) -> std::result::Result<(), BackendError>
|
||||
) -> std::result::Result<String, BackendError>
|
||||
where
|
||||
F: FnMut(&StreamEvent) -> Status,
|
||||
{
|
||||
@@ -154,7 +158,7 @@ where
|
||||
.collect::<std::result::Result<Vec<_>, BackendError>>()?;
|
||||
let mut callback_dispatch_failed = false;
|
||||
|
||||
dispatch_prepared_stream_with_pipeline(
|
||||
let provider_id = dispatch_prepared_stream_with_pipeline(
|
||||
client,
|
||||
&mut adapter_routes,
|
||||
|| aborted.load(Ordering::Relaxed),
|
||||
@@ -174,7 +178,7 @@ where
|
||||
"{STREAM_CALLBACK_DISPATCH_FAILED_REASON}:unknown"
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
Ok(provider_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +199,16 @@ pub(crate) fn emit_error_event(callback: &ThreadsafeFunction<String, ()>, messag
|
||||
let _ = callback.call(Ok(error_event), ThreadsafeFunctionCallMode::NonBlocking);
|
||||
}
|
||||
|
||||
pub(crate) fn emit_provider_selected_event(callback: &ThreadsafeFunction<String, ()>, provider_id: String) {
|
||||
let event = serde_json::json!({
|
||||
"type": "provider_selected",
|
||||
"provider_id": provider_id,
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let _ = callback.call(Ok(event), ThreadsafeFunctionCallMode::NonBlocking);
|
||||
}
|
||||
|
||||
fn emit_stream_event(callback: &ThreadsafeFunction<String, ()>, event: &StreamEvent) -> Status {
|
||||
let value = serde_json::to_string(event).unwrap_or_else(|error| {
|
||||
serde_json::json!({
|
||||
|
||||
@@ -14,7 +14,10 @@ use napi::{
|
||||
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||
};
|
||||
|
||||
use super::callback::{NapiEventSink, NapiToolExecutor, emit_tool_loop_event};
|
||||
use super::{
|
||||
super::emit_provider_selected_event,
|
||||
callback::{NapiEventSink, NapiToolExecutor, emit_tool_loop_event},
|
||||
};
|
||||
use crate::llm::{
|
||||
LlmDispatchPayload, LlmMiddlewarePayload, LlmStreamHandle, STREAM_ABORTED_REASON,
|
||||
STREAM_CALLBACK_DISPATCH_FAILED_REASON, STREAM_END_MARKER, StreamPipeline, apply_request_middlewares,
|
||||
@@ -39,11 +42,13 @@ fn dispatch_prepared_round_with_fallback(
|
||||
})
|
||||
.collect::<std::result::Result<Vec<_>, BackendError>>()?;
|
||||
|
||||
run_prepared_stream_round_with_fallback(
|
||||
let mut selected_provider_id: Option<String> = None;
|
||||
let outcome = run_prepared_stream_round_with_fallback(
|
||||
&mut pipelines,
|
||||
|on_event| {
|
||||
let (selected_index, _) =
|
||||
let (selected_index, provider_id) =
|
||||
dispatch_prepared_stream_with_fallback_index(&DefaultHttpClient::default(), &adapter_routes, on_event)?;
|
||||
selected_provider_id = Some(provider_id);
|
||||
Ok(selected_index)
|
||||
},
|
||||
|| aborted.load(Ordering::Relaxed),
|
||||
@@ -53,7 +58,11 @@ fn dispatch_prepared_round_with_fallback(
|
||||
emitted.store(true, Ordering::Relaxed);
|
||||
emit_tool_loop_event(callback, loop_event)
|
||||
},
|
||||
)
|
||||
)?;
|
||||
if let Some(provider_id) = selected_provider_id {
|
||||
emit_provider_selected_event(callback, provider_id);
|
||||
}
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
fn prepare_tool_loop_route(
|
||||
|
||||
Reference in New Issue
Block a user