Add experimental thread prediction protocol types (#49480)

## What changed

Define `thread/prediction/request` with `threadId` and `sourceTurnId`, and `thread/prediction/updated` with a `completed` result containing optional text or a `failed` result.

Update generated JSON schemas, TypeScript types, and Python notification models. Route prediction notifications to their thread in the TUI and ignore them in the chat widget.

The request remains unimplemented and returns a method-not-found error.

GitOrigin-RevId: 09922f583d5c56f808d018121c651dcd92734515
This commit is contained in:
Keyan Zhang
2026-09-30 04:48:50 +00:00
committed by copyberry
parent 15fd656ddb
commit 90abcfac02
20 changed files with 439 additions and 2 deletions
@@ -5474,6 +5474,67 @@
],
"type": "object"
},
"ThreadPredictionResult": {
"oneOf": [
{
"properties": {
"text": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"completed"
],
"title": "CompletedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CompletedThreadPredictionResult",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"failed"
],
"title": "FailedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "FailedThreadPredictionResult",
"type": "object"
}
]
},
"ThreadPredictionUpdatedNotification": {
"properties": {
"result": {
"$ref": "#/definitions/ThreadPredictionResult"
},
"sourceTurnId": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"result",
"sourceTurnId",
"threadId"
],
"type": "object"
},
"ThreadProjectUpdatedNotification": {
"properties": {
"projectId": {
@@ -7153,6 +7214,26 @@
"title": "Thread/goal/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/prediction/updated"
],
"title": "Thread/prediction/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadPredictionUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/prediction/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -4839,6 +4839,26 @@
"title": "Thread/goal/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/prediction/updated"
],
"title": "Thread/prediction/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadPredictionUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/prediction/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -23019,6 +23039,69 @@
"title": "ThreadNameUpdatedNotification",
"type": "object"
},
"ThreadPredictionResult": {
"oneOf": [
{
"properties": {
"text": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"completed"
],
"title": "CompletedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CompletedThreadPredictionResult",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"failed"
],
"title": "FailedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "FailedThreadPredictionResult",
"type": "object"
}
]
},
"ThreadPredictionUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"result": {
"$ref": "#/definitions/v2/ThreadPredictionResult"
},
"sourceTurnId": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"result",
"sourceTurnId",
"threadId"
],
"title": "ThreadPredictionUpdatedNotification",
"type": "object"
},
"ThreadProjectUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -16281,6 +16281,26 @@
"title": "Thread/goal/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/prediction/updated"
],
"title": "Thread/prediction/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadPredictionUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/prediction/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -20733,6 +20753,69 @@
"title": "ThreadNameUpdatedNotification",
"type": "object"
},
"ThreadPredictionResult": {
"oneOf": [
{
"properties": {
"text": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"completed"
],
"title": "CompletedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CompletedThreadPredictionResult",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"failed"
],
"title": "FailedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "FailedThreadPredictionResult",
"type": "object"
}
]
},
"ThreadPredictionUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"result": {
"$ref": "#/definitions/ThreadPredictionResult"
},
"sourceTurnId": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"result",
"sourceTurnId",
"threadId"
],
"title": "ThreadPredictionUpdatedNotification",
"type": "object"
},
"ThreadProjectUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -0,0 +1,65 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ThreadPredictionResult": {
"oneOf": [
{
"properties": {
"text": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"completed"
],
"title": "CompletedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CompletedThreadPredictionResult",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"failed"
],
"title": "FailedThreadPredictionResultType",
"type": "string"
}
},
"required": [
"type"
],
"title": "FailedThreadPredictionResult",
"type": "object"
}
]
}
},
"properties": {
"result": {
"$ref": "#/definitions/ThreadPredictionResult"
},
"sourceTurnId": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"result",
"sourceTurnId",
"threadId"
],
"title": "ThreadPredictionUpdatedNotification",
"type": "object"
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ThreadPredictionResult = { "type": "completed", text: string | null, } | { "type": "failed" };
@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ThreadPredictionResult } from "./ThreadPredictionResult";
export type ThreadPredictionUpdatedNotification = { threadId: string, sourceTurnId: string, result: ThreadPredictionResult, };
@@ -526,6 +526,8 @@ export type { ThreadMetadataGitInfoUpdateParams } from "./ThreadMetadataGitInfoU
export type { ThreadMetadataUpdateParams } from "./ThreadMetadataUpdateParams";
export type { ThreadMetadataUpdateResponse } from "./ThreadMetadataUpdateResponse";
export type { ThreadNameUpdatedNotification } from "./ThreadNameUpdatedNotification";
export type { ThreadPredictionResult } from "./ThreadPredictionResult";
export type { ThreadPredictionUpdatedNotification } from "./ThreadPredictionUpdatedNotification";
export type { ThreadProjectUpdatedNotification } from "./ThreadProjectUpdatedNotification";
export type { ThreadQueueChangedNotification } from "./ThreadQueueChangedNotification";
export type { ThreadReadParams } from "./ThreadReadParams";
@@ -605,6 +605,12 @@ client_request_definitions! {
serialization: thread_id(params.thread_id),
response: v2::ThreadSetNameResponse,
},
#[experimental("thread/prediction/request")]
ThreadPredictionRequest => "thread/prediction/request" {
params: v2::ThreadPredictionRequestParams,
serialization: thread_id(params.thread_id),
response: v2::ThreadPredictionRequestResponse,
},
ThreadGoalSet => "thread/goal/set" {
params: v2::ThreadGoalSetParams,
serialization: thread_id(params.thread_id),
@@ -1927,6 +1933,8 @@ server_notification_definitions! {
ThreadNameUpdated => "thread/name/updated" (v2::ThreadNameUpdatedNotification),
ThreadAttachmentUpdated => "thread/attachment/updated" (v2::ThreadAttachmentUpdatedNotification),
ThreadGoalUpdated => "thread/goal/updated" (v2::ThreadGoalUpdatedNotification),
#[experimental("thread/prediction/updated")]
ThreadPredictionUpdated => "thread/prediction/updated" (v2::ThreadPredictionUpdatedNotification),
ThreadGoalCleared => "thread/goal/cleared" (v2::ThreadGoalClearedNotification),
#[experimental("thread/queue/changed")]
ThreadQueueChanged => "thread/queue/changed" (v2::ThreadQueueChangedNotification),
@@ -34,6 +34,7 @@ mod rollout;
mod thread;
mod thread_attachment;
mod thread_data;
mod thread_prediction;
mod thread_usage;
mod turn;
mod user_verification;
@@ -74,6 +75,7 @@ pub use shared::*;
pub use thread::*;
pub use thread_attachment::*;
pub use thread_data::*;
pub use thread_prediction::*;
pub use thread_usage::*;
pub use turn::*;
pub use user_verification::*;
@@ -0,0 +1,35 @@
//! App-server API types for predictions.
use crate::JsonSchema;
use crate::TS;
use serde::Deserialize;
use serde::Serialize;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadPredictionRequestParams {
pub thread_id: String,
pub source_turn_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[ts(export_to = "v2/")]
pub struct ThreadPredictionRequestResponse {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadPredictionUpdatedNotification {
pub thread_id: String,
pub source_turn_id: String,
pub result: ThreadPredictionResult,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase")]
#[ts(tag = "type", rename_all = "camelCase", export_to = "v2/")]
pub enum ThreadPredictionResult {
Completed { text: Option<String> },
Failed,
}
@@ -11,6 +11,7 @@ use crate::current_time::app_server_time_provider;
use crate::error_code::internal_error;
use crate::error_code::invalid_params;
use crate::error_code::invalid_request;
use crate::error_code::method_not_found;
use crate::extensions::ThreadExtensionDependencies;
use crate::extensions::app_server_extension_event_sink;
use crate::extensions::thread_extensions;
@@ -1443,6 +1444,9 @@ impl MessageProcessor {
.thread_unarchive(request_id.clone(), params)
.await
}
ClientRequest::ThreadPredictionRequest { .. } => Err(method_not_found(
"thread/prediction/request is not implemented yet",
)),
ClientRequest::ThreadCompactStart { params, .. } => {
self.thread_processor
.thread_compact_start(&request_id, params)
@@ -66,6 +66,7 @@ pub(crate) fn without_notification_media(notification: ServerNotification) -> Se
| ServerNotification::ThreadNameUpdated(_)
| ServerNotification::ThreadAttachmentUpdated(_)
| ServerNotification::ThreadGoalUpdated(_)
| ServerNotification::ThreadPredictionUpdated(_)
| ServerNotification::ThreadGoalCleared(_)
| ServerNotification::ThreadQueueChanged(_)
| ServerNotification::ProjectChanged(_)
@@ -71,6 +71,9 @@ pub(super) fn server_notification_thread_target(
ServerNotification::ThreadGoalUpdated(notification) => {
Some(notification.thread_id.as_str())
}
ServerNotification::ThreadPredictionUpdated(notification) => {
Some(notification.thread_id.as_str())
}
ServerNotification::ThreadGoalCleared(notification) => {
Some(notification.thread_id.as_str())
}
+1
View File
@@ -361,6 +361,7 @@ impl ChatWidget {
| ServerNotification::ThreadStatusChanged(_)
| ServerNotification::ThreadReverted(_)
| ServerNotification::ThreadQueueChanged(_)
| ServerNotification::ThreadPredictionUpdated(_)
| ServerNotification::ThreadArchived(_)
| ServerNotification::ThreadDeleted(_)
| ServerNotification::ThreadUnarchived(_)
@@ -61,6 +61,7 @@ from .v2_all import ThreadDeletedNotification
from .v2_all import ThreadGoalClearedNotification
from .v2_all import ThreadGoalUpdatedNotification
from .v2_all import ThreadNameUpdatedNotification
from .v2_all import ThreadPredictionUpdatedNotification
from .v2_all import ThreadProjectUpdatedNotification
from .v2_all import ThreadQueueChangedNotification
from .v2_all import ThreadRealtimeClosedNotification
@@ -144,6 +145,7 @@ KnownNotificationPayload: TypeAlias = (
| ThreadGoalClearedNotification
| ThreadGoalUpdatedNotification
| ThreadNameUpdatedNotification
| ThreadPredictionUpdatedNotification
| ThreadProjectUpdatedNotification
| ThreadQueueChangedNotification
| ThreadRealtimeClosedNotification
@@ -230,6 +232,7 @@ NOTIFICATION_MODELS: dict[str, type[KnownNotificationPayload]] = {
"thread/goal/cleared": ThreadGoalClearedNotification,
"thread/goal/updated": ThreadGoalUpdatedNotification,
"thread/name/updated": ThreadNameUpdatedNotification,
"thread/prediction/updated": ThreadPredictionUpdatedNotification,
"thread/project/updated": ThreadProjectUpdatedNotification,
"thread/queue/changed": ThreadQueueChangedNotification,
"thread/realtime/closed": ThreadRealtimeClosedNotification,
@@ -5613,6 +5613,39 @@ class ThreadNameUpdatedNotification(BaseModel):
thread_name: Annotated[str | None, Field(alias="threadName")] = None
class CompletedThreadPredictionResult(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
text: str | None = None
type: Annotated[Literal["completed"], Field(title="CompletedThreadPredictionResultType")]
class FailedThreadPredictionResult(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
type: Annotated[Literal["failed"], Field(title="FailedThreadPredictionResultType")]
class ThreadPredictionResult(
RootModel[CompletedThreadPredictionResult | FailedThreadPredictionResult]
):
model_config = ConfigDict(
populate_by_name=True,
)
root: CompletedThreadPredictionResult | FailedThreadPredictionResult
class ThreadPredictionUpdatedNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
result: ThreadPredictionResult
source_turn_id: Annotated[str, Field(alias="sourceTurnId")]
thread_id: Annotated[str, Field(alias="threadId")]
class ThreadProjectUpdatedNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -9178,6 +9211,24 @@ class ThreadAttachmentUpdatedServerNotification(BaseModel):
params: ThreadAttachmentUpdatedNotification
class ThreadPredictionUpdatedServerNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
emitted_at_ms: Annotated[
int | None,
Field(
alias="emittedAtMs",
description="Unix timestamp (in milliseconds) when app-server emitted this notification.",
),
] = None
method: Annotated[
Literal["thread/prediction/updated"],
Field(title="Thread/prediction/updatedNotificationMethod"),
]
params: ThreadPredictionUpdatedNotification
class ThreadGoalClearedServerNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -12859,6 +12910,7 @@ class ServerNotification(
| ThreadNameUpdatedServerNotification
| ThreadAttachmentUpdatedServerNotification
| ThreadGoalUpdatedServerNotification
| ThreadPredictionUpdatedServerNotification
| ThreadGoalClearedServerNotification
| ThreadQueueChangedServerNotification
| ProjectChangedServerNotification
@@ -12948,6 +13000,7 @@ class ServerNotification(
| ThreadNameUpdatedServerNotification
| ThreadAttachmentUpdatedServerNotification
| ThreadGoalUpdatedServerNotification
| ThreadPredictionUpdatedServerNotification
| ThreadGoalClearedServerNotification
| ThreadQueueChangedServerNotification
| ProjectChangedServerNotification