mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-04 02:52:55 +08:00
Merge branch 'main' of github.com:leookun/cursor-byok
This commit is contained in:
@@ -1,9 +1,4 @@
|
||||
以下是重构后完整目标版本
|
||||
实现时,先创建所有目录和文件固化,每个文件头部都写好注释再实现
|
||||
旧服务已被备份为server_backup,/Users/leokun/Documents/cursor-byok/server 目录已创建
|
||||
行数均为目标估算,使用 `≈` 标记;不包含测试、生成代码和空行。
|
||||
实现时可做略微调整,测试要求相对于目标文件旁边的独立文件,禁止码内测试
|
||||
本文档目录 /Users/leokun/Documents/cursor-byok/cursor.md
|
||||
|
||||
## 完整目录
|
||||
|
||||
```text
|
||||
@@ -656,54 +651,7 @@ store ─X→ cursor
|
||||
model ─X→ cursor
|
||||
```
|
||||
|
||||
## 当前代码迁移
|
||||
|
||||
```text
|
||||
当前 目标
|
||||
|
||||
cursor/bidi_append.rs → api/cursor/bidi.rs
|
||||
cursor/run_sse.rs → api/cursor/run_sse.rs
|
||||
cursor/handlers.rs → api/cursor/handlers.rs
|
||||
cursor/proxy.rs → api/cursor/proxy.rs
|
||||
|
||||
cursor/sessions.rs → cursor/transport/registry.rs
|
||||
+ cursor/transport/handle.rs
|
||||
+ cursor/transport/output.rs
|
||||
|
||||
cursor/inbox.rs → cursor/transport/inbox.rs
|
||||
|
||||
cursor/actor.rs → cursor/transport/
|
||||
+ cursor/conversation/runtime.rs
|
||||
+ cursor/conversation/delivery.rs
|
||||
|
||||
cursor/session.rs → cursor/conversation/runtime.rs
|
||||
+ cursor/conversation/output.rs
|
||||
+ cursor/checkpoint/
|
||||
+ cursor/tools/
|
||||
|
||||
cursor/request/prepare.rs → cursor/compile/run.rs
|
||||
cursor/request/context.rs → cursor/compile/context.rs
|
||||
cursor/request/background.rs → cursor/compile/insert_messages.rs
|
||||
cursor/request/runtime.rs → cursor/compile/break_messages.rs
|
||||
cursor/request/images.rs → cursor/compile/images.rs
|
||||
cursor/request/model.rs → cursor/compile/model.rs
|
||||
|
||||
cursor/interaction/mod.rs → cursor/protocol/events.rs
|
||||
cursor/interaction/query.rs → cursor/tools/codec/query.rs
|
||||
cursor/interaction/render.rs → cursor/tools/codec/render.rs
|
||||
|
||||
cursor/projection/decode.rs → cursor/checkpoint/messages/decode.rs
|
||||
cursor/projection/encode.rs → cursor/checkpoint/messages/encode.rs
|
||||
cursor/projection/tests.rs → cursor/checkpoint/messages/tests.rs
|
||||
|
||||
cursor/presentation.rs → cursor/checkpoint/steps.rs
|
||||
|
||||
run/runtime.rs RunRegistry → cursor/conversation/registry.rs
|
||||
run/runtime.rs RunActor → run/engine.rs + run/handle.rs
|
||||
run/port.rs → run/command.rs + run/event.rs + run/port.rs
|
||||
|
||||
store/revisions.rs → store/checkpoints.rs
|
||||
```
|
||||
|
||||
|
||||
## 最终核心
|
||||
@@ -719,4 +667,4 @@ Bidi
|
||||
→ Checkpoint
|
||||
→ Transport
|
||||
→ RunSSE
|
||||
```
|
||||
```
|
||||
|
||||
@@ -343,7 +343,14 @@ impl ConversationOutput {
|
||||
let call = calls.get_mut(&index).ok_or_else(|| {
|
||||
Error::Protocol(format!("unknown completed tool index: {index}"))
|
||||
})?;
|
||||
call.arguments = serde_json::from_str(&call.arguments_text)?;
|
||||
// A tool call with no arguments streams no argument text.
|
||||
// Treat empty text as an empty object, matching the model
|
||||
// cycle, instead of failing the run on `from_str("")`.
|
||||
call.arguments = if call.arguments_text.trim().is_empty() {
|
||||
serde_json::json!({})
|
||||
} else {
|
||||
serde_json::from_str(&call.arguments_text)?
|
||||
};
|
||||
}
|
||||
RunEvent::Usage(usage) => {
|
||||
if !self.context.compacting {
|
||||
|
||||
@@ -101,7 +101,14 @@ impl Store {
|
||||
.bind(&call.call_id)
|
||||
.bind(&call.model_call_id)
|
||||
.bind(&call.name)
|
||||
.bind(&call.arguments_text)
|
||||
// A no-argument tool call streams no argument text; persist it as an
|
||||
// empty object so the `arguments_json` column always holds valid JSON
|
||||
// and can be re-parsed on load.
|
||||
.bind(if call.arguments_text.trim().is_empty() {
|
||||
"{}"
|
||||
} else {
|
||||
call.arguments_text.as_str()
|
||||
})
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -506,6 +506,68 @@ async fn runtime_user_message_action_interrupts_and_continues_with_new_message()
|
||||
assert!(history.contains("queued follow-up"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tool_call_with_empty_arguments_does_not_fail_the_run() {
|
||||
// A tool call that carries no arguments streams no argument text. Parsing it
|
||||
// as JSON must yield an empty object (as the model cycle already does), not
|
||||
// fail the run with `EOF while parsing a value`.
|
||||
let (_directory, store) = fixtures::temp_store().await;
|
||||
let provider = fake_provider::FakeProvider::default();
|
||||
provider.push(tool_response("call-1", "UpdateCurrentStep", ""));
|
||||
provider.push(text_response("done after empty-argument tool"));
|
||||
let assets = PromptAssets::load(
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("prompt/cursor")
|
||||
.as_path(),
|
||||
)
|
||||
.unwrap();
|
||||
let registry = TransportRegistry::new(
|
||||
store,
|
||||
Arc::new(provider.clone()),
|
||||
PromptCompiler::new(assets),
|
||||
);
|
||||
let handle = registry.get_or_create("empty-args-request").await.unwrap();
|
||||
let mut output = handle.subscribe();
|
||||
handle
|
||||
.command(TransportCommand::Append {
|
||||
seqno: 0,
|
||||
message: Box::new(client_run_for(
|
||||
"empty-args-request",
|
||||
"empty-args-conversation",
|
||||
)),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut append_seqno = 1;
|
||||
let mut saw_done = false;
|
||||
loop {
|
||||
let frame = tokio::time::timeout(std::time::Duration::from_secs(5), output.recv())
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("RunSSE closed before successful EndStream");
|
||||
let (flags, payload) = connect::decode_frames(&frame).unwrap().pop().unwrap();
|
||||
if flags & connect::END_STREAM_FLAG != 0 {
|
||||
assert_eq!(
|
||||
payload.as_ref(),
|
||||
b"{}",
|
||||
"run failed: {}",
|
||||
String::from_utf8_lossy(&payload)
|
||||
);
|
||||
break;
|
||||
}
|
||||
let server = pb::AgentServerMessage::decode(payload).unwrap();
|
||||
if let Some(pb::agent_server_message::Message::InteractionUpdate(update)) = server.message {
|
||||
if let Some(pb::interaction_update::Message::TextDelta(delta)) = update.message {
|
||||
saw_done |= delta.text.contains("done after empty-argument tool");
|
||||
}
|
||||
}
|
||||
acknowledge_kv(&handle, &mut append_seqno, &frame).await;
|
||||
}
|
||||
assert!(saw_done);
|
||||
assert_eq!(provider.requests().len(), 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn injected_user_context_restarts_only_the_active_model_cycle() {
|
||||
let (_directory, store) = fixtures::temp_store().await;
|
||||
|
||||
Reference in New Issue
Block a user