1+ #pragma once
2+ #include < json/json.h>
3+ #include < optional>
4+ #include < string>
5+ #include < unordered_set>
6+ #include < vector>
7+ #include " utils/curl_utils.h"
8+ #include " utils/result.hpp"
9+ namespace environment_utils {
10+
11+ constexpr const auto kBaseEnvironmentsUrl =
12+ " https://delta.jan.ai/environments/" ;
13+
14+ struct Environment {
15+ std::string type; // e.g., "python"
16+ std::string name; // e.g., "whispervq"
17+ std::string version; // e.g., "latest"
18+ std::string os; // e.g., "window", "linux"
19+ std::string arch; // e.g., "amd64"
20+
21+ // Convert Environment to JSON
22+ Json::Value ToJson () const {
23+ Json::Value json;
24+ json[" type" ] = type;
25+ json[" name" ] = name;
26+ json[" version" ] = version;
27+ json[" os" ] = os;
28+ json[" arch" ] = arch;
29+ return json;
30+ }
31+
32+ // Create Environment from JSON
33+ static cpp::result<Environment, std::string> FromJson (
34+ const Json::Value& json) {
35+ Environment env;
36+
37+ // Validate required fields
38+ const std::vector<std::string> required_fields = {" type" , " name" , " version" ,
39+ " os" , " arch" };
40+
41+ for (const auto & field : required_fields) {
42+ if (!json.isMember (field) || json[field].asString ().empty ()) {
43+ return cpp::fail (" Missing or empty required field: " + field);
44+ }
45+ }
46+
47+ env.type = json[" type" ].asString ();
48+ env.name = json[" name" ].asString ();
49+ env.version = json[" version" ].asString ();
50+ env.os = json[" os" ].asString ();
51+ env.arch = json[" arch" ].asString ();
52+
53+ return env;
54+ }
55+
56+ // Method to generate full artifact URL
57+ std::string generateUrl () const {
58+ return kBaseEnvironmentsUrl + type + " /" + name + " /" + version + " /" +
59+ name + " -" + os + " -" + arch + " .zip" ;
60+ }
61+
62+ // Method to validate the environment structure
63+ bool isValid () const {
64+ return !type.empty () && !name.empty () && !version.empty () && !os.empty () &&
65+ !arch.empty ();
66+ }
67+ };
68+
69+ // Utility function to parse URL components into an Environment struct
70+ cpp::result<Environment, std::string> parseEnvironmentUrl (
71+ const std::string& url) {
72+ Environment env;
73+
74+ size_t environments_pos = url.find (" environments/" );
75+ if (environments_pos == std::string::npos) {
76+ return cpp::fail (" Invalid URL format" );
77+ }
78+
79+ std::string remaining = url.substr (environments_pos + 13 );
80+ std::vector<std::string> parts;
81+ size_t pos = 0 ;
82+ while ((pos = remaining.find (' /' )) != std::string::npos) {
83+ parts.push_back (remaining.substr (0 , pos));
84+ remaining.erase (0 , pos + 1 );
85+ }
86+ parts.push_back (remaining);
87+
88+ if (parts.size () < 5 ) {
89+ return cpp::fail (" Insufficient URL components" );
90+ }
91+
92+ env.type = parts[0 ];
93+ env.name = parts[1 ];
94+ env.version = parts[2 ];
95+
96+ // Extract OS and arch from the filename
97+ std::string filename = parts[3 ];
98+ size_t os_sep = filename.find (' -' );
99+ size_t arch_sep = filename.find (' -' , os_sep + 1 );
100+
101+ if (os_sep == std::string::npos || arch_sep == std::string::npos) {
102+ return cpp::fail (" Cannot parse OS and architecture" );
103+ }
104+
105+ env.os = filename.substr (os_sep + 1 , arch_sep - os_sep - 1 );
106+ env.arch = filename.substr (arch_sep + 1 , filename.find (' .' ) - arch_sep - 1 );
107+
108+ return env;
109+ }
110+
111+ // Fetch environment names
112+ cpp::result<std::vector<std::string>, std::string> fetchEnvironmentNames (
113+ const std::string& type, int timeout = 30 ) {
114+ auto url = kBaseEnvironmentsUrl + type;
115+ auto json_result = curl_utils::SimpleGetJson (url, timeout, false );
116+ if (json_result.has_error ()) {
117+ return cpp::fail (json_result.error ());
118+ }
119+
120+ std::vector<std::string> environment_names;
121+ const Json::Value& root = json_result.value ();
122+
123+ // Store unique environment names
124+ std::unordered_set<std::string> unique_names;
125+
126+ for (const auto & item : root) {
127+ if (item.isMember (" path" )) {
128+ environment_names.push_back (item[" path" ].asString ());
129+ }
130+ }
131+
132+ return environment_names;
133+ }
134+
135+ // Get all versions for a specific environment
136+ cpp::result<std::vector<std::string>, std::string> fetchEnvironmentVersions (
137+ const std::string& base_url, const std::string& environment_name,
138+ int timeout = 30 , bool recursive = true ) {
139+ auto json_result = curl_utils::SimpleGetJson (
140+ base_url + " /" + environment_name, timeout, recursive);
141+ if (json_result.has_error ()) {
142+ return cpp::fail (json_result.error ());
143+ }
144+
145+ std::vector<std::string> versions;
146+ const Json::Value& root = json_result.value ();
147+
148+ // Store unique versions
149+ std::unordered_set<std::string> unique_versions;
150+
151+ for (const auto & item : root) {
152+ if (item.isMember (" path" )) {
153+ auto url_parse_result = parseEnvironmentUrl (
154+ base_url + " /" + environment_name + " /" + item[" path" ].asString ());
155+ if (!url_parse_result.has_error ()) {
156+ const auto & env = url_parse_result.value ();
157+ // Only add if not already present
158+ if (unique_versions.insert (env.version ).second ) {
159+ versions.push_back (env.version );
160+ }
161+ }
162+ }
163+ }
164+
165+ return versions;
166+ }
167+
168+ } // namespace environment_utils
0 commit comments