Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions c/csrc/include/longbridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -2610,6 +2610,14 @@ typedef struct lb_conversation_response_t {
* Number of references
*/
uintptr_t num_references;
/**
* Suggested follow-up questions ("you might also ask"); empty when absent
*/
const char *const *further_questions;
/**
* Number of follow-up questions
*/
uintptr_t num_further_questions;
/**
* Run duration in seconds
*/
Expand Down
10 changes: 10 additions & 0 deletions c/src/agent_context/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ pub struct CConversationResponse {
pub references: *const CReference,
/// Number of references
pub num_references: usize,
/// Suggested follow-up questions ("you might also ask"); empty when absent
pub further_questions: *const *const c_char,
/// Number of follow-up questions
pub num_further_questions: usize,
/// Run duration in seconds
pub elapsed_time: f64,
/// Present only when `status` is `ConversationStatusInterrupted` (can be
Expand All @@ -511,6 +515,7 @@ pub(crate) struct CConversationResponseOwned {
status: CConversationStatus,
answer: CString,
references: CVec<CReferenceOwned>,
further_questions: CVec<CString>,
elapsed_time: f64,
interrupt: Option<CCow<CInterruptOwned>>,
error: Option<CCow<CAgentErrorOwned>>,
Expand All @@ -524,6 +529,7 @@ impl From<ConversationResponse> for CConversationResponseOwned {
status,
answer,
references,
further_questions,
elapsed_time,
interrupt,
error,
Expand All @@ -537,6 +543,7 @@ impl From<ConversationResponse> for CConversationResponseOwned {
// distinction between "absent" and "empty" here, both surface as
// `num_references == 0`.
references: references.unwrap_or_default().into(),
further_questions: further_questions.unwrap_or_default().into(),
elapsed_time,
interrupt: interrupt.map(CCow::new),
error: error.map(CCow::new),
Expand All @@ -554,6 +561,7 @@ impl ToFFI for CConversationResponseOwned {
status,
answer,
references,
further_questions,
elapsed_time,
interrupt,
error,
Expand All @@ -565,6 +573,8 @@ impl ToFFI for CConversationResponseOwned {
answer: answer.to_ffi_type(),
references: references.to_ffi_type(),
num_references: references.len(),
further_questions: further_questions.to_ffi_type(),
num_further_questions: further_questions.len(),
elapsed_time: *elapsed_time,
interrupt: interrupt
.as_ref()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class ConversationResponse {
private ConversationStatus status;
private String answer;
private Reference[] references;
private String[] furtherQuestions;
private double elapsedTime;
private Interrupt interrupt;
private ConversationError error;
Expand Down Expand Up @@ -64,6 +65,15 @@ public Reference[] getReferences() {
return references;
}

/**
* Returns the suggested follow-up questions ("you might also ask").
*
* @return suggested follow-up questions
*/
public String[] getFurtherQuestions() {
return furtherQuestions;
}

/**
* Returns the run duration in seconds.
*
Expand Down Expand Up @@ -95,7 +105,8 @@ public ConversationError getError() {
@Override
public String toString() {
return "ConversationResponse [chatUid=" + chatUid + ", messageId=" + messageId + ", status=" + status
+ ", answer=" + answer + ", references=" + Arrays.toString(references) + ", elapsedTime="
+ elapsedTime + ", interrupt=" + interrupt + ", error=" + error + "]";
+ ", answer=" + answer + ", references=" + Arrays.toString(references) + ", furtherQuestions="
+ Arrays.toString(furtherQuestions) + ", elapsedTime=" + elapsedTime + ", interrupt=" + interrupt
+ ", error=" + error + "]";
}
}
13 changes: 9 additions & 4 deletions java/src/types/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3315,16 +3315,18 @@ impl_java_class!(
);

/// JNI-side view of [`longbridge::agent::ConversationResponse`], with
/// `references` normalized from `Option<Vec<Reference>>` down to a plain
/// `Vec` (empty when absent) so it can use the same `#[java(objarray)]`
/// convention as every other list field — mirrors how `StockPosition` above
/// collapses `Option<Decimal>`/`Option<i64>` fields with `unwrap_or_default`.
/// `references`/`further_questions` normalized from `Option<Vec<_>>` down to
/// a plain `Vec` (empty when absent) so they can use the same
/// `#[java(objarray)]` convention as every other list field — mirrors how
/// `StockPosition` above collapses `Option<Decimal>`/`Option<i64>` fields
/// with `unwrap_or_default`.
pub(crate) struct ConversationResponse {
pub(crate) chat_uid: String,
pub(crate) message_id: String,
pub(crate) status: longbridge::agent::ConversationStatus,
pub(crate) answer: String,
pub(crate) references: Vec<longbridge::agent::Reference>,
pub(crate) further_questions: Vec<String>,
pub(crate) elapsed_time: f64,
pub(crate) interrupt: Option<longbridge::agent::Interrupt>,
pub(crate) error: Option<longbridge::agent::AgentError>,
Expand All @@ -3338,6 +3340,7 @@ impl From<longbridge::agent::ConversationResponse> for ConversationResponse {
status: value.status,
answer: value.answer,
references: value.references.unwrap_or_default(),
further_questions: value.further_questions.unwrap_or_default(),
elapsed_time: value.elapsed_time,
interrupt: value.interrupt,
error: value.error,
Expand All @@ -3355,6 +3358,8 @@ impl_java_class!(
answer,
#[java(objarray)]
references,
#[java(objarray)]
further_questions,
elapsed_time,
interrupt,
error
Expand Down
2 changes: 2 additions & 0 deletions nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4063,6 +4063,8 @@ export interface ConversationResponse {
answer: string
/** Sources referenced by the answer */
references?: Array<Reference>
/** Suggested follow-up questions */
furtherQuestions?: Array<string>
/** Run duration in seconds */
elapsedTime: number
/** Present only when `status` is `interrupted` */
Expand Down
3 changes: 3 additions & 0 deletions nodejs/src/agent/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pub struct ConversationResponse {
pub answer: String,
/// Sources referenced by the answer
pub references: Option<Vec<Reference>>,
/// Suggested follow-up questions
pub further_questions: Option<Vec<String>>,
/// Run duration in seconds
pub elapsed_time: f64,
/// Present only when `status` is `interrupted`
Expand All @@ -257,6 +259,7 @@ impl From<lb::ConversationResponse> for ConversationResponse {
references: v
.references
.map(|refs| refs.into_iter().map(Into::into).collect()),
further_questions: v.further_questions,
elapsed_time: v.elapsed_time,
interrupt: v.interrupt.map(Into::into),
error: v.error.map(Into::into),
Expand Down
2 changes: 2 additions & 0 deletions python/pysrc/longbridge/openapi.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13481,6 +13481,8 @@ class ConversationResponse:
"""Final answer text; valid when status is ConversationStatus.Succeeded"""
references: list[Reference] | None
"""Sources referenced by the answer"""
further_questions: list[str] | None
"""Suggested follow-up questions ("you might also ask")"""
elapsed_time: float
"""Run duration in seconds"""
interrupt: Interrupt | None
Expand Down
3 changes: 3 additions & 0 deletions python/src/agent/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ pub(crate) struct ConversationResponse {
pub status: ConversationStatus,
pub answer: String,
pub references: Option<Vec<Reference>>,
/// Suggested follow-up questions ("you might also ask")
pub further_questions: Option<Vec<String>>,
pub elapsed_time: f64,
pub interrupt: Option<Interrupt>,
pub error: Option<AgentError>,
Expand All @@ -233,6 +235,7 @@ impl From<longbridge::agent::ConversationResponse> for ConversationResponse {
references: v
.references
.map(|refs| refs.into_iter().map(Into::into).collect()),
further_questions: v.further_questions,
elapsed_time: v.elapsed_time,
interrupt: v.interrupt.map(Into::into),
error: v.error.map(Into::into),
Expand Down
24 changes: 23 additions & 1 deletion rust/src/agent/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ pub struct ConversationResponse {
/// Sources referenced by the answer
#[serde(default)]
pub references: Option<Vec<Reference>>,
/// Suggested follow-up questions ("you might also ask"); present when the
/// run produced them
#[serde(default)]
pub further_questions: Option<Vec<String>>,
/// Run duration in seconds
#[serde(default)]
pub elapsed_time: f64,
Expand Down Expand Up @@ -252,6 +256,7 @@ impl ConversationResponse {
status: payload.status,
answer: payload.outputs.answer.unwrap_or_default(),
references: payload.outputs.references,
further_questions: payload.outputs.further_questions,
elapsed_time: payload.elapsed_time,
interrupt: None,
error,
Expand All @@ -277,6 +282,7 @@ impl ConversationResponse {
status: ConversationStatus::Interrupted,
answer: String::new(),
references: None,
further_questions: None,
elapsed_time: 0.0,
interrupt: Some(interrupt),
error: None,
Expand Down Expand Up @@ -340,6 +346,10 @@ pub struct WorkflowOutputs {
/// Sources referenced by the answer
#[serde(default)]
pub references: Option<Vec<Reference>>,
/// Suggested follow-up questions ("you might also ask"); present when the
/// run produced them
#[serde(default)]
pub further_questions: Option<Vec<String>>,
}

/// Payload of a `workflow_finished` SSE event. `status` is never
Expand Down Expand Up @@ -1065,14 +1075,24 @@ mod tests {

#[test]
fn deserialize_workflow_finished_payload() {
let json = r#"{"status":"succeeded","elapsed_time":3.21,"outputs":{"answer":"Tesla (TSLA.US) recently..."}}"#;
let json = r#"{"status":"succeeded","elapsed_time":3.21,"outputs":{"answer":"Tesla (TSLA.US) recently...","further_questions":["What is Tesla's P/E?","How did Q3 deliveries look?"]}}"#;
let payload: WorkflowFinishedPayload = serde_json::from_str(json).unwrap();
assert_eq!(payload.status, ConversationStatus::Succeeded);
assert!((payload.elapsed_time - 3.21).abs() < f64::EPSILON);
assert_eq!(
payload.outputs.answer.as_deref(),
Some("Tesla (TSLA.US) recently...")
);
assert_eq!(
payload.outputs.further_questions.as_deref(),
Some(
[
"What is Tesla's P/E?".to_string(),
"How did Q3 deliveries look?".to_string(),
]
.as_slice()
)
);

let resp = ConversationResponse::from_stream_parts(
Some(("ct_9f2c1a5b".to_string(), "42".to_string())),
Expand All @@ -1081,6 +1101,8 @@ mod tests {
assert_eq!(resp.chat_uid, "ct_9f2c1a5b");
assert_eq!(resp.message_id, "42");
assert_eq!(resp.answer, "Tesla (TSLA.US) recently...");
// Follow-up questions thread through the folded response.
assert_eq!(resp.further_questions.as_ref().unwrap().len(), 2);
assert!(resp.interrupt.is_none());
assert!(resp.error.is_none());
}
Expand Down
Loading