按接口收紧Tripo任务结果类型

为 text、image、multiview 三类生成定义独立必填结果结构

以枚举关联任务类型与具体结果并对缺失字段失败关闭

将通用 URL 类型由 TripoModelUrl 更名为 TripoUrl
This commit is contained in:
2026-09-18 21:37:38 +08:00
parent a4beb53dc6
commit 527706b84e
11 changed files with 101 additions and 69 deletions
@@ -1,8 +1,8 @@
use tripo3d_sdk::TripoClient;
use super::{
TripoDownloadedModel, TripoError, TripoModelUrl, TripoSettings, TripoTaskHandle,
TripoTaskSnapshot, map_task, validate_task_id,
TripoDownloadedModel, TripoError, TripoSettings, TripoTaskHandle, TripoTaskSnapshot, TripoUrl,
map_task, validate_task_id,
};
pub struct TripoProviderClient {
@@ -49,7 +49,7 @@ impl TripoProviderClient {
task_id: handle.task_id.clone(),
message: "completed task has no model URL".into(),
})?;
let url = TripoModelUrl::parse(&downloaded.url).map_err(|error| match error {
let url = TripoUrl::parse(&downloaded.url).map_err(|error| match error {
TripoError::OutputSchema { message, .. } => TripoError::OutputSchema {
task_id: handle.task_id.clone(),
message,
@@ -2,8 +2,13 @@ use shared_contracts::model3d::common::Model3dTaskStatus;
use tripo3d_sdk::{Task, TaskOutput, TaskStatus};
use super::{
TripoError, TripoModelUrl, TripoTaskFailure, TripoTaskHandle, TripoTaskOutput,
TripoTaskSnapshot, TripoTaskType,
TripoError, TripoTaskFailure, TripoTaskHandle, TripoTaskOutput, TripoTaskSnapshot,
TripoTaskType, TripoUrl,
};
use crate::{
image_to_model::result::TripoImageToModelResult,
multiview_to_model::result::TripoMultiviewToModelResult,
text_to_model::result::TripoTextToModelResult,
};
pub(crate) fn map_task(task: Task) -> Result<TripoTaskSnapshot, TripoError> {
@@ -13,6 +18,7 @@ pub(crate) fn map_task(task: Task) -> Result<TripoTaskSnapshot, TripoError> {
let output = match status {
Model3dTaskStatus::Completed => Some(map_output(
&task_id,
&task_type,
task.output
.as_ref()
.ok_or_else(|| TripoError::OutputSchema {
@@ -66,52 +72,51 @@ fn map_status(status: TaskStatus) -> Result<Model3dTaskStatus, TripoError> {
}
}
fn map_output(task_id: &str, output: &TaskOutput) -> Result<TripoTaskOutput, TripoError> {
let mut model_urls = Vec::new();
for candidate in output
.model_urls
.iter()
.flat_map(|urls| urls.iter())
.chain(output.model_url.iter())
.chain(output.model.iter())
.chain(output.pbr_model.iter())
.chain(output.base_model.iter())
{
if !model_urls
.iter()
.any(|url: &TripoModelUrl| url.as_str() == candidate)
{
model_urls.push(parse_url(candidate, task_id)?);
}
}
if model_urls.is_empty() {
return Err(TripoError::OutputSchema {
task_id: task_id.into(),
message: "completed task output contains no model URL".into(),
});
}
fn map_output(
task_id: &str,
task_type: &TripoTaskType,
output: &TaskOutput,
) -> Result<TripoTaskOutput, TripoError> {
let model_url = required_url(task_id, "model_url", output.model_url.as_deref())?;
let rendered_image_url = required_url(
task_id,
"rendered_image_url",
output.rendered_image_url.as_deref(),
)?;
let rendered_image_url = output
.rendered_image_url
.as_ref()
.or(output.rendered_image.as_ref())
.map(|value| parse_url(value, task_id))
.transpose()?;
let generated_image_url = output
.generated_image_url
.as_ref()
.map(|value| parse_url(value, task_id))
.transpose()?;
Ok(TripoTaskOutput {
model_urls,
rendered_image_url,
generated_image_url,
})
match task_type {
TripoTaskType::TextToModel => Ok(TripoTaskOutput::TextToModel(TripoTextToModelResult {
model_url,
rendered_image_url,
generated_image_url: required_url(
task_id,
"generated_image_url",
output.generated_image_url.as_deref(),
)?,
})),
TripoTaskType::ImageToModel => Ok(TripoTaskOutput::ImageToModel(TripoImageToModelResult {
model_url,
rendered_image_url,
})),
TripoTaskType::MultiviewToModel => Ok(TripoTaskOutput::MultiviewToModel(
TripoMultiviewToModelResult {
model_url,
rendered_image_url,
},
)),
}
}
fn parse_url(value: &str, task_id: &str) -> Result<TripoModelUrl, TripoError> {
TripoModelUrl::parse(value).map_err(|error| match error {
fn required_url(task_id: &str, field: &str, value: Option<&str>) -> Result<TripoUrl, TripoError> {
let value = value.ok_or_else(|| TripoError::OutputSchema {
task_id: task_id.into(),
message: format!("completed task output is missing required {field}"),
})?;
parse_url(value, task_id)
}
fn parse_url(value: &str, task_id: &str) -> Result<TripoUrl, TripoError> {
TripoUrl::parse(value).map_err(|error| match error {
TripoError::OutputSchema { message, .. } => TripoError::OutputSchema {
task_id: task_id.into(),
message,
@@ -10,8 +10,8 @@ pub use config::TripoSettings;
pub use error::{TripoError, TripoField, TripoValidationReason};
pub(crate) use mapping::map_task;
pub use types::{
TripoDownloadedModel, TripoModelUrl, TripoTaskFailure, TripoTaskHandle, TripoTaskOutput,
TripoTaskSnapshot, TripoTaskType,
TripoDownloadedModel, TripoTaskFailure, TripoTaskHandle, TripoTaskOutput, TripoTaskSnapshot,
TripoTaskType, TripoUrl,
};
pub(crate) use validation::{
TripoGenerationOptions, validate_generation_options, validate_task_id,
@@ -4,7 +4,11 @@ use url::Url;
use shared_contracts::model3d::common::Model3dTaskStatus;
use crate::common::TripoError;
use crate::{
common::TripoError, image_to_model::result::TripoImageToModelResult,
multiview_to_model::result::TripoMultiviewToModelResult,
text_to_model::result::TripoTextToModelResult,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoTaskHandle {
@@ -19,18 +23,18 @@ pub enum TripoTaskType {
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoModelUrl(Url);
pub struct TripoUrl(Url);
impl TripoModelUrl {
impl TripoUrl {
pub(crate) fn parse(value: &str) -> Result<Self, TripoError> {
let url = Url::parse(value).map_err(|error| TripoError::OutputSchema {
task_id: "unknown".into(),
message: format!("model URL is invalid: {error}"),
message: format!("URL is invalid: {error}"),
})?;
if !matches!(url.scheme(), "http" | "https") {
return Err(TripoError::OutputSchema {
task_id: "unknown".into(),
message: "model URL must use http or https".into(),
message: "URL must use http or https".into(),
});
}
Ok(Self(url))
@@ -41,7 +45,7 @@ impl TripoModelUrl {
}
}
impl fmt::Display for TripoModelUrl {
impl fmt::Display for TripoUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
@@ -54,12 +58,10 @@ pub struct TripoTaskFailure {
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoTaskOutput {
pub model_urls: Vec<TripoModelUrl>,
pub rendered_image_url: Option<TripoModelUrl>,
pub generated_image_url: Option<TripoModelUrl>,
// TODO(tripo-docs): add fixed per-task output fields after the provider
// contract is supplied and verified against real task responses.
pub enum TripoTaskOutput {
TextToModel(TripoTextToModelResult),
ImageToModel(TripoImageToModelResult),
MultiviewToModel(TripoMultiviewToModelResult),
}
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -76,7 +78,7 @@ pub struct TripoTaskSnapshot {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoDownloadedModel {
pub url: TripoModelUrl,
pub url: TripoUrl,
pub content_type: Option<String>,
pub data: Vec<u8>,
}
@@ -1 +1,2 @@
mod client;
pub mod result;
@@ -0,0 +1,7 @@
use crate::common::TripoUrl;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoImageToModelResult {
pub model_url: TripoUrl,
pub rendered_image_url: TripoUrl,
}
+6 -6
View File
@@ -4,12 +4,12 @@
//! DTOs and `TripoError`, never `tripo3d_sdk` types or its poller API.
mod common;
mod image_to_model;
mod multiview_to_model;
mod text_to_model;
pub mod image_to_model;
pub mod multiview_to_model;
pub mod text_to_model;
pub use common::{
TripoDownloadedModel, TripoError, TripoField, TripoModelUrl, TripoProviderClient,
TripoSettings, TripoTaskFailure, TripoTaskHandle, TripoTaskOutput, TripoTaskSnapshot,
TripoTaskType, TripoValidationReason,
TripoDownloadedModel, TripoError, TripoField, TripoProviderClient, TripoSettings,
TripoTaskFailure, TripoTaskHandle, TripoTaskOutput, TripoTaskSnapshot, TripoTaskType, TripoUrl,
TripoValidationReason,
};
@@ -1 +1,2 @@
mod client;
pub mod result;
@@ -0,0 +1,7 @@
use crate::common::TripoUrl;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoMultiviewToModelResult {
pub model_url: TripoUrl,
pub rendered_image_url: TripoUrl,
}
@@ -1,2 +1,3 @@
mod client;
pub mod result;
mod validation;
@@ -0,0 +1,8 @@
use crate::common::TripoUrl;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TripoTextToModelResult {
pub model_url: TripoUrl,
pub rendered_image_url: TripoUrl,
pub generated_image_url: TripoUrl,
}