feat: propagate workspace prompt revisions

This commit is contained in:
2026-08-19 06:01:03 +09:00
parent d24d50cac9
commit 17d6789b41
7 changed files with 351 additions and 7 deletions
+82 -2
View File
@@ -2,8 +2,8 @@ use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use worker::{
WorkspaceClient, WorkspaceClientError, WorkspaceRequest, WorkspaceRequestMethod,
WorkspaceResponse,
WorkspaceClient, WorkspaceClientError, WorkspacePromptProjection, WorkspaceRequest,
WorkspaceRequestMethod, WorkspaceResponse,
};
use crate::auth::{
@@ -12,6 +12,7 @@ use crate::auth::{
WorkerMutationSourceClaims, new_token_id,
};
use crate::runtime::RuntimeWorkspaceScope;
use crate::worker_backend::WorkspacePromptProjectionCache;
pub const DEFAULT_WORKER_MUTATION_SOURCE_TTL_SECONDS: u64 = 60;
@@ -293,6 +294,7 @@ pub struct RuntimeOwnedWorkspaceClient {
worker_id: String,
request_timeout: Option<Duration>,
worker_remove: Option<RuntimeWorkerMutationForwarder>,
prompt_projection_cache: Option<Arc<WorkspacePromptProjectionCache>>,
}
impl RuntimeOwnedWorkspaceClient {
@@ -309,6 +311,7 @@ impl RuntimeOwnedWorkspaceClient {
worker_id: worker_id.into(),
request_timeout: None,
worker_remove: None,
prompt_projection_cache: None,
}
}
@@ -317,6 +320,14 @@ impl RuntimeOwnedWorkspaceClient {
self
}
pub(crate) fn with_prompt_projection_cache(
mut self,
cache: Arc<WorkspacePromptProjectionCache>,
) -> Self {
self.prompt_projection_cache = Some(cache);
self
}
#[cfg(test)]
fn with_request_timeout(mut self, request_timeout: Option<Duration>) -> Self {
self.request_timeout = request_timeout;
@@ -385,6 +396,40 @@ impl WorkspaceClient for RuntimeOwnedWorkspaceClient {
}
}
fn current_prompt_projection(
&self,
) -> Result<Option<WorkspacePromptProjection>, WorkspaceClientError> {
let Some(cache) = self.prompt_projection_cache.as_ref() else {
return Ok(None);
};
if let Some(projection) = cache
.active(&self.workspace_id)
.map_err(WorkspaceClientError::Request)?
{
return Ok(Some((*projection).clone()));
}
let response = self.execute(WorkspaceRequest::get(format!(
"/api/w/{}/config-sources/active/prompt-projection",
self.workspace_id
)))?;
if !(200..300).contains(&response.status) {
return Err(WorkspaceClientError::Request(format!(
"active Workspace Prompt projection request failed with HTTP {}: {}",
response.status, response.body
)));
}
let projection: WorkspacePromptProjection =
serde_json::from_str(&response.body).map_err(|error| {
WorkspaceClientError::Request(format!(
"invalid active Workspace Prompt projection response: {error}"
))
})?;
let projection = cache
.observe(projection)
.map_err(WorkspaceClientError::Request)?;
Ok(Some((*projection).clone()))
}
fn execute_worker_remove(
&self,
target_runtime_id: &str,
@@ -521,6 +566,41 @@ mod tests {
verify_worker_mutation_source_proof,
};
#[test]
fn current_prompt_projection_uses_the_shared_runtime_cache_without_http() {
let cache = Arc::new(WorkspacePromptProjectionCache::default());
let catalog = worker::EffectivePromptCatalog::new(
std::collections::BTreeMap::from([(
"default".to_string(),
"workspace prompt".to_string(),
)]),
3,
"schema",
"toolchain",
)
.unwrap();
let projection = WorkspacePromptProjection::new(
"workspace-a",
"source-3",
catalog.catalog_digest.clone(),
catalog,
)
.unwrap();
cache.observe(projection).unwrap();
let client = RuntimeOwnedWorkspaceClient::new(
"workspace-a",
"http://127.0.0.1:1",
"runtime-a",
"worker-a",
)
.with_prompt_projection_cache(cache);
let projection = client.current_prompt_projection().unwrap().unwrap();
assert_eq!(projection.config_revision, 3);
assert_eq!(projection.source_digest, "source-3");
}
#[test]
fn ordinary_workspace_forwarding_stamps_legacy_source_only_inside_runtime() {
use std::io::{Read, Write};