entrenar/ecosystem/ruchy/
artifact.rs1use super::error::RuchyBridgeError;
4use super::session::EntrenarSession;
5use crate::research::{ArtifactType, Author, ContributorRole, License, ResearchArtifact};
6
7pub fn session_to_artifact(
11 session: &EntrenarSession,
12) -> Result<ResearchArtifact, RuchyBridgeError> {
13 if !session.has_training_data() && session.code_history.is_empty() {
14 return Err(RuchyBridgeError::NoTrainingHistory);
15 }
16
17 let mut artifact =
18 ResearchArtifact::new(&session.id, &session.name, ArtifactType::Notebook, License::Mit);
19
20 if let Some(ref user) = session.user {
22 let author = Author::new(user)
23 .with_role(ContributorRole::Software)
24 .with_role(ContributorRole::Investigation);
25 artifact = artifact.with_author(author);
26 }
27
28 let description = build_session_description(session);
30 artifact = artifact.with_description(description);
31
32 if session.tags.is_empty() {
34 artifact = artifact.with_keywords(["training", "experiment", "entrenar"]);
35 } else {
36 artifact = artifact.with_keywords(session.tags.iter().map(String::as_str));
37 }
38
39 let steps = session.metrics.total_steps();
41 artifact = artifact.with_version(format!("1.0.0+steps{steps}"));
42
43 Ok(artifact)
44}
45
46pub(crate) fn build_session_description(session: &EntrenarSession) -> String {
48 let mut parts = Vec::new();
49
50 if let Some(ref arch) = session.model_architecture {
51 parts.push(format!("Model: {arch}"));
52 }
53
54 if let Some(ref dataset) = session.dataset_id {
55 parts.push(format!("Dataset: {dataset}"));
56 }
57
58 let steps = session.metrics.total_steps();
59 if steps > 0 {
60 parts.push(format!("Training steps: {steps}"));
61 }
62
63 if let Some(loss) = session.metrics.final_loss() {
64 parts.push(format!("Final loss: {loss:.4}"));
65 }
66
67 if let Some(acc) = session.metrics.final_accuracy() {
68 parts.push(format!("Final accuracy: {acc:.2}%"));
69 }
70
71 if let Some(duration) = session.duration() {
72 let hours = duration.num_hours();
73 let minutes = duration.num_minutes() % 60;
74 parts.push(format!("Duration: {hours}h {minutes}m"));
75 }
76
77 if parts.is_empty() {
78 format!("Training session from Ruchy ({})", session.id)
79 } else {
80 parts.join(". ")
81 }
82}