@@ -65,6 +65,23 @@ static int printe(const char * fmt, ...) {
6565 return ret;
6666}
6767
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+
6885class Opt {
6986 public:
7087 int init (int argc, const char ** argv) {
@@ -698,6 +715,39 @@ class LlamaData {
698715 return download (url, bn, true );
699716 }
700717
718+ int s3_dl (const std::string & model, const std::string & bn) {
719+ const size_t slash_pos = model.find (' /' );
720+ if (slash_pos == std::string::npos) {
721+ return 1 ;
722+ }
723+
724+ const std::string bucket = model.substr (0 , slash_pos);
725+ const std::string key = model.substr (slash_pos + 1 );
726+ const char * access_key = std::getenv (" AWS_ACCESS_KEY_ID" );
727+ const char * secret_key = std::getenv (" AWS_SECRET_ACCESS_KEY" );
728+ if (!access_key || !secret_key) {
729+ printe (" AWS credentials not found in environment\n " );
730+ return 1 ;
731+ }
732+
733+ // Generate AWS Signature Version 4 headers
734+ // (Implementation requires HMAC-SHA256 and date handling)
735+ // Get current timestamp
736+ const time_t now = time (nullptr );
737+ const tm tm = *gmtime (&now);
738+ const std::string date = strftime_fmt (" %Y%m%d" , tm);
739+ const std::string datetime = strftime_fmt (" %Y%m%dT%H%M%SZ" , tm);
740+ const std::vector<std::string> headers = {
741+ " Authorization: AWS4-HMAC-SHA256 Credential=" + std::string (access_key) + " /" + date +
742+ " /us-east-1/s3/aws4_request" ,
743+ " x-amz-content-sha256: UNSIGNED-PAYLOAD" , " x-amz-date: " + datetime
744+ };
745+
746+ const std::string url = " https://" + bucket + " .s3.amazonaws.com/" + key;
747+
748+ return download (url, bn, true , headers);
749+ }
750+
701751 std::string basename (const std::string & path) {
702752 const size_t pos = path.find_last_of (" /\\ " );
703753 if (pos == std::string::npos) {
@@ -738,6 +788,9 @@ class LlamaData {
738788 rm_until_substring (model_, " github:" );
739789 rm_until_substring (model_, " ://" );
740790 ret = github_dl (model_, bn);
791+ } else if (string_starts_with (model_, " s3://" )) {
792+ rm_until_substring (model_, " ://" );
793+ ret = s3_dl (model_, bn);
741794 } else { // ollama:// or nothing
742795 rm_until_substring (model_, " ollama.com/library/" );
743796 rm_until_substring (model_, " ://" );
0 commit comments