Skip to content

Commit beebd7c

Browse files
committed
feat(functions): preserve prompt environments in pull output
1 parent 3f59754 commit beebd7c

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

src/functions/pull.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ struct PullFunctionRow {
4444
#[serde(default)]
4545
function_data: Option<Value>,
4646
#[serde(default)]
47+
environments: Option<Value>,
48+
#[serde(default)]
4749
created: Option<String>,
4850
#[serde(default)]
4951
_xact_id: Option<String>,
@@ -64,6 +66,7 @@ struct NormalizedPrompt {
6466
tools: Option<Value>,
6567
raw_tools_json: Option<String>,
6668
tool_functions: Option<Value>,
69+
environments: Option<Value>,
6770
}
6871

6972
#[derive(Debug)]
@@ -1065,6 +1068,11 @@ fn normalize_prompt_row(row: &PullFunctionRow) -> Result<NormalizedPrompt> {
10651068
.get("tool_functions")
10661069
.filter(|value| !is_empty_render_value(value))
10671070
.cloned();
1071+
let environments = row
1072+
.environments
1073+
.as_ref()
1074+
.or_else(|| prompt_data.get("environments"))
1075+
.and_then(normalize_environment_slugs);
10681076
let version = row
10691077
._xact_id
10701078
.as_deref()
@@ -1086,6 +1094,7 @@ fn normalize_prompt_row(row: &PullFunctionRow) -> Result<NormalizedPrompt> {
10861094
tools,
10871095
raw_tools_json,
10881096
tool_functions,
1097+
environments,
10891098
})
10901099
}
10911100

@@ -1171,6 +1180,12 @@ fn render_project_file_ts(
11711180
format_ts_value(tool_functions, 2)
11721181
));
11731182
}
1183+
if let Some(environments) = &row.environments {
1184+
body_lines.push(format!(
1185+
" environments: {},",
1186+
format_ts_value(environments, 2)
1187+
));
1188+
}
11741189

11751190
out.push_str(&format!(
11761191
"export const {var_name} = project.prompts.create({{\n"
@@ -1268,6 +1283,12 @@ fn render_project_file_py(
12681283
format_py_value(tool_functions, 4)
12691284
));
12701285
}
1286+
if let Some(environments) = &row.environments {
1287+
out.push_str(&format!(
1288+
" environments={},\n",
1289+
format_py_value(environments, 4)
1290+
));
1291+
}
12711292
out.push_str(")\n\n");
12721293
}
12731294

@@ -1444,6 +1465,30 @@ fn should_unquote_object_key(key: &str) -> bool {
14441465
chars.all(|ch| ch == '$' || ch == '_' || ch.is_ascii_alphanumeric())
14451466
}
14461467

1468+
fn normalize_environment_slugs(value: &Value) -> Option<Value> {
1469+
let items = value.as_array()?;
1470+
let mut slugs = Vec::new();
1471+
for item in items {
1472+
let slug = item
1473+
.as_object()
1474+
.and_then(|object| object.get("slug"))
1475+
.and_then(Value::as_str)
1476+
.or_else(|| item.as_str())
1477+
.map(str::trim)
1478+
.filter(|value| !value.is_empty())
1479+
.map(ToOwned::to_owned);
1480+
if let Some(slug) = slug {
1481+
slugs.push(Value::String(slug));
1482+
}
1483+
}
1484+
1485+
if slugs.is_empty() {
1486+
None
1487+
} else {
1488+
Some(Value::Array(slugs))
1489+
}
1490+
}
1491+
14471492
fn is_empty_render_value(value: &Value) -> bool {
14481493
match value {
14491494
Value::Null => true,
@@ -1600,6 +1645,7 @@ mod tests {
16001645
description: None,
16011646
prompt_data: None,
16021647
function_data: None,
1648+
environments: None,
16031649
created: None,
16041650
_xact_id: None,
16051651
};
@@ -1631,6 +1677,7 @@ mod tests {
16311677
description: None,
16321678
prompt_data: None,
16331679
function_data: None,
1680+
environments: None,
16341681
created: None,
16351682
_xact_id: None,
16361683
},
@@ -1643,6 +1690,7 @@ mod tests {
16431690
description: None,
16441691
prompt_data: None,
16451692
function_data: None,
1693+
environments: None,
16461694
created: None,
16471695
_xact_id: None,
16481696
},
@@ -1655,6 +1703,7 @@ mod tests {
16551703
description: None,
16561704
prompt_data: None,
16571705
function_data: None,
1706+
environments: None,
16581707
created: None,
16591708
_xact_id: None,
16601709
},
@@ -1689,6 +1738,7 @@ mod tests {
16891738
description: None,
16901739
prompt_data: None,
16911740
function_data: None,
1741+
environments: None,
16921742
created: None,
16931743
_xact_id: None,
16941744
};
@@ -1715,6 +1765,7 @@ mod tests {
17151765
description: None,
17161766
prompt_data: None,
17171767
function_data: None,
1768+
environments: None,
17181769
created: None,
17191770
_xact_id: None,
17201771
};
@@ -1787,6 +1838,10 @@ mod tests {
17871838
]
17881839
})),
17891840
function_data: Some(serde_json::json!({ "type": "prompt" })),
1841+
environments: Some(serde_json::json!([
1842+
{ "slug": "staging" },
1843+
{ "slug": "production" }
1844+
])),
17901845
created: None,
17911846
_xact_id: Some("123".to_string()),
17921847
};
@@ -1809,6 +1864,9 @@ mod tests {
18091864
assert!(rendered.contains(" version=\"123\","));
18101865
assert!(rendered.contains("messages=["));
18111866
assert!(rendered.contains("model=\"gpt-4o-mini\""));
1867+
assert!(rendered.contains("environments=["));
1868+
assert!(rendered.contains("\"staging\""));
1869+
assert!(rendered.contains("\"production\""));
18121870
}
18131871

18141872
#[test]
@@ -1832,6 +1890,9 @@ mod tests {
18321890
}
18331891
})),
18341892
function_data: Some(serde_json::json!({ "type": "prompt" })),
1893+
environments: Some(serde_json::json!([
1894+
{ "slug": "test-env" }
1895+
])),
18351896
created: None,
18361897
_xact_id: Some("123".to_string()),
18371898
};
@@ -1851,6 +1912,23 @@ mod tests {
18511912
assert!(rendered.contains("export const basicMath = project.prompts.create({"));
18521913
assert!(rendered.contains(" id: \"f1\","));
18531914
assert!(rendered.contains(" version: \"123\","));
1915+
assert!(rendered.contains("environments: ["));
1916+
assert!(rendered.contains("\"test-env\""));
1917+
}
1918+
1919+
#[test]
1920+
fn normalize_environment_slugs_supports_objects_and_strings() {
1921+
let raw = serde_json::json!([
1922+
{ "slug": " staging " },
1923+
"production",
1924+
{ "slug": "" },
1925+
{ "foo": "bar" }
1926+
]);
1927+
1928+
assert_eq!(
1929+
normalize_environment_slugs(&raw),
1930+
Some(serde_json::json!(["staging", "production"]))
1931+
);
18541932
}
18551933

18561934
#[test]

0 commit comments

Comments
 (0)