@@ -65,6 +65,23 @@ static int printe(const char * fmt, ...) {
65
65
return ret;
66
66
}
67
67
68
+ static std::string strftime_fmt (const char * fmt, const std::tm & tm) {
69
+ // Estimate the size of the output buffer
70
+ std::string buffer;
71
+ buffer.resize (128 );
72
+
73
+ // Try to format the string
74
+ size_t len;
75
+ while ((len = std::strftime (buffer.data (), buffer.size (), fmt, &tm)) == 0 ) {
76
+ // If the buffer was too small, double its size and try again
77
+ buffer.resize (buffer.size () * 2 );
78
+ }
79
+
80
+ buffer.resize (len);
81
+
82
+ return buffer;
83
+ }
84
+
68
85
class Opt {
69
86
public:
70
87
int init (int argc, const char ** argv) {
@@ -698,6 +715,46 @@ class LlamaData {
698
715
return download (url, bn, true );
699
716
}
700
717
718
+ int s3_dl (const std::string & model, const std::string & bn) {
719
+ const std::string prefix = " s3://" ;
720
+ const size_t pos = model.find (prefix);
721
+ if (pos != 0 ) {
722
+ return 1 ;
723
+ }
724
+
725
+ const std::string path = model.substr (prefix.length ());
726
+ const size_t slash_pos = path.find (' /' );
727
+ if (slash_pos == std::string::npos) {
728
+ return 1 ;
729
+ }
730
+
731
+ const std::string bucket = path.substr (0 , slash_pos);
732
+ const std::string key = path.substr (slash_pos + 1 );
733
+ const char * access_key = std::getenv (" AWS_ACCESS_KEY_ID" );
734
+ const char * secret_key = std::getenv (" AWS_SECRET_ACCESS_KEY" );
735
+ if (!access_key || !secret_key) {
736
+ printe (" AWS credentials not found in environment\n " );
737
+ return 1 ;
738
+ }
739
+
740
+ // Generate AWS Signature Version 4 headers
741
+ // (Implementation requires HMAC-SHA256 and date handling)
742
+ // Get current timestamp
743
+ const time_t now = time (nullptr );
744
+ const tm tm = *gmtime (&now);
745
+ const std::string date = strftime_fmt (" %Y%m%d" , tm);
746
+ const std::string datetime = strftime_fmt (" %Y%m%dT%H%M%SZ" , tm);
747
+ const std::vector<std::string> headers = {
748
+ " Authorization: AWS4-HMAC-SHA256 Credential=" + std::string (access_key) + " /" + date +
749
+ " /us-east-1/s3/aws4_request" ,
750
+ " x-amz-content-sha256: UNSIGNED-PAYLOAD" , " x-amz-date: " + datetime
751
+ };
752
+
753
+ const std::string url = " https://" + bucket + " .s3.amazonaws.com/" + key;
754
+
755
+ return download (url, bn, true , headers);
756
+ }
757
+
701
758
std::string basename (const std::string & path) {
702
759
const size_t pos = path.find_last_of (" /\\ " );
703
760
if (pos == std::string::npos) {
@@ -738,6 +795,9 @@ class LlamaData {
738
795
rm_until_substring (model_, " github:" );
739
796
rm_until_substring (model_, " ://" );
740
797
ret = github_dl (model_, bn);
798
+ } else if (string_starts_with (model_, " s3://" )) {
799
+ rm_until_substring (model_, " ://" );
800
+ ret = s3_dl (model_, bn);
741
801
} else { // ollama:// or nothing
742
802
rm_until_substring (model_, " ollama.com/library/" );
743
803
rm_until_substring (model_, " ://" );
0 commit comments