Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Translations:

JuliaOS is a comprehensive framework for building decentralized applications (DApps) with a focus on agent-based architectures, swarm intelligence, and cross-chain operations. It provides both a CLI interface for quick deployment and a framework API for custom implementations. By leveraging AI-powered agents and swarm optimization, JuliaOS enables sophisticated strategies across multiple blockchains.

**πŸ†• New Storage Enhancements**: JuliaOS now supports decentralized storage backends including IPFS and Arweave, enabling agents to store and share data across distributed networks. Agents can seamlessly upload LLM outputs, datasets, and swarm state snapshots to any configured storage provider.

## Documentation

- πŸ“– [Overview](https://juliaos.gitbook.io/juliaos-documentation-hub): Project overview and vision
Expand Down
6 changes: 6 additions & 0 deletions backend/src/agents/tools/Tools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ include("telegram/tool_detect_swearing.jl")
include("telegram/tool_send_message.jl")
include("tool_scrape_article_text.jl")
include("tool_summarize_for_post.jl")
include("tool_file_upload.jl")
include("tool_file_download.jl")
include("tool_storage_manage.jl")

using ..CommonTypes: ToolSpecification

Expand All @@ -37,5 +40,8 @@ register_tool(TOOL_DETECT_SWEAR_SPECIFICATION)
register_tool(TOOL_SEND_MESSAGE_SPECIFICATION)
register_tool(TOOL_SCRAPE_ARTICLE_TEXT_SPECIFICATION)
register_tool(TOOL_SUMMARIZE_FOR_POST_SPECIFICATION)
register_tool(TOOL_FILE_UPLOAD_SPECIFICATION)
register_tool(TOOL_FILE_DOWNLOAD_SPECIFICATION)
register_tool(TOOL_STORAGE_MANAGE_SPECIFICATION)

end
128 changes: 128 additions & 0 deletions backend/src/agents/tools/tool_file_download.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using ....framework.JuliaOSFramework.Storage
using ..CommonTypes: ToolSpecification, ToolMetadata, ToolConfig
using JSON3, Dates, Logging

Base.@kwdef struct ToolFileDownloadConfig <: ToolConfig
include_metadata::Bool = true # Include metadata in response
max_download_size::Int = 50 * 1024 * 1024 # 50MB default max download size
end

"""
tool_file_download(cfg::ToolFileDownloadConfig, task::Dict)

Download a file from the configured storage backend.

Expected task parameters:
- key: The storage key of the file to download

Returns:
- success: Boolean indicating if download was successful
- key: The storage key that was requested
- data: The file data (if successful)
- metadata: File metadata (if include_metadata is true and successful)
- message: Success or error message
- provider: The storage provider used
- size: Size of the downloaded data
"""
function tool_file_download(cfg::ToolFileDownloadConfig, task::Dict)
try
# Validate required parameters
if !haskey(task, "key")
return Dict(
"success" => false,
"message" => "Missing required parameter: key",
"error_code" => "MISSING_KEY"
)
end

key = task["key"]

if isempty(key)
return Dict(
"success" => false,
"message" => "Storage key cannot be empty",
"error_code" => "EMPTY_KEY"
)
end

# Check if file exists
if !Storage.exists_default(key)
return Dict(
"success" => false,
"message" => "File not found: $key",
"error_code" => "FILE_NOT_FOUND",
"key" => key
)
end

# Load file from storage
result = Storage.load_default(key)

if isnothing(result)
return Dict(
"success" => false,
"message" => "Failed to load file: $key",
"error_code" => "LOAD_FAILED",
"key" => key
)
end

data, metadata = result

# Calculate data size for response
data_json = JSON3.write(data)
data_size = length(data_json)

# Check download size limit
if data_size > cfg.max_download_size
return Dict(
"success" => false,
"message" => "File too large to download. Size: $data_size bytes, Max: $(cfg.max_download_size) bytes",
"error_code" => "FILE_TOO_LARGE",
"key" => key,
"size" => data_size,
"max_size" => cfg.max_download_size
)
end

provider_type = Storage.get_current_provider_type()
@info "File downloaded successfully via tool. Key: $key, Size: $data_size bytes, Provider: $provider_type"

# Prepare response
response = Dict(
"success" => true,
"message" => "File downloaded successfully",
"key" => key,
"data" => data,
"size" => data_size,
"provider" => string(provider_type)
)

# Include metadata if requested
if cfg.include_metadata
response["metadata"] = metadata
end

return response

catch e
@error "Error in file download tool" exception=(e, catch_backtrace())
return Dict(
"success" => false,
"message" => "Download failed: $(sprint(showerror, e))",
"error_code" => "TOOL_ERROR",
"key" => get(task, "key", "unknown")
)
end
end

const TOOL_FILE_DOWNLOAD_METADATA = ToolMetadata(
"file_download",
"Download files from the configured storage backend (local, IPFS, Arweave, etc.)"
)

const TOOL_FILE_DOWNLOAD_SPECIFICATION = ToolSpecification(
tool_file_download,
ToolFileDownloadConfig,
TOOL_FILE_DOWNLOAD_METADATA
)
147 changes: 147 additions & 0 deletions backend/src/agents/tools/tool_file_upload.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using ....framework.JuliaOSFramework.Storage
using ..CommonTypes: ToolSpecification, ToolMetadata, ToolConfig
using JSON3, Dates, Logging

Base.@kwdef struct ToolFileUploadConfig <: ToolConfig
max_file_size::Int = 10 * 1024 * 1024 # 10MB default
allowed_extensions::Vector{String} = String[] # Empty means all extensions allowed
auto_generate_key::Bool = true # Auto-generate key if not provided
end

"""
tool_file_upload(cfg::ToolFileUploadConfig, task::Dict)

Upload a file to the configured storage backend.

Expected task parameters:
- data: The file data to upload (can be string, dict, or any JSON-serializable data)
- key: (optional) Storage key for the file. If not provided and auto_generate_key is true, will generate one
- filename: (optional) Original filename for metadata
- metadata: (optional) Additional metadata to store with the file

Returns:
- success: Boolean indicating if upload was successful
- key: The storage key where the file was saved
- message: Success or error message
- provider: The storage provider used
- size: Size of the uploaded data
"""
function tool_file_upload(cfg::ToolFileUploadConfig, task::Dict)
try
# Validate required parameters
if !haskey(task, "data")
return Dict(
"success" => false,
"message" => "Missing required parameter: data",
"error_code" => "MISSING_DATA"
)
end

data = task["data"]

# Serialize data to JSON for size checking
data_json = JSON3.write(data)
data_size = length(data_json)

# Check file size
if data_size > cfg.max_file_size
return Dict(
"success" => false,
"message" => "File too large. Size: $data_size bytes, Max: $(cfg.max_file_size) bytes",
"error_code" => "FILE_TOO_LARGE",
"size" => data_size,
"max_size" => cfg.max_file_size
)
end

# Generate or use provided key
key = if haskey(task, "key") && !isempty(task["key"])
task["key"]
elseif cfg.auto_generate_key
"upload_$(now())_$(rand(UInt32))"
else
return Dict(
"success" => false,
"message" => "No storage key provided and auto_generate_key is disabled",
"error_code" => "MISSING_KEY"
)
end

# Prepare metadata
metadata = Dict{String, Any}(
"uploaded_at" => string(now(Dates.UTC)),
"upload_tool" => "file_upload",
"size" => data_size,
"data_type" => string(typeof(data))
)

# Add optional metadata from task
if haskey(task, "metadata") && isa(task["metadata"], Dict)
merge!(metadata, task["metadata"])
end

# Add filename if provided
if haskey(task, "filename")
metadata["filename"] = task["filename"]

# Check file extension if restrictions are configured
if !isempty(cfg.allowed_extensions)
filename = task["filename"]
ext = lowercase(splitext(filename)[2])
if !isempty(ext) && !(ext in cfg.allowed_extensions)
return Dict(
"success" => false,
"message" => "File extension '$ext' not allowed. Allowed: $(cfg.allowed_extensions)",
"error_code" => "INVALID_EXTENSION",
"extension" => ext,
"allowed_extensions" => cfg.allowed_extensions
)
end
end
end

# Upload to storage
success = Storage.save_default(key, data; metadata=metadata)

if success
provider_type = Storage.get_current_provider_type()
@info "File uploaded successfully via tool. Key: $key, Size: $data_size bytes, Provider: $provider_type"

return Dict(
"success" => true,
"message" => "File uploaded successfully",
"key" => key,
"size" => data_size,
"provider" => string(provider_type),
"metadata" => metadata
)
else
@error "Failed to upload file via tool. Key: $key"
return Dict(
"success" => false,
"message" => "Failed to save file to storage",
"error_code" => "STORAGE_ERROR",
"key" => key
)
end

catch e
@error "Error in file upload tool" exception=(e, catch_backtrace())
return Dict(
"success" => false,
"message" => "Upload failed: $(sprint(showerror, e))",
"error_code" => "TOOL_ERROR"
)
end
end

const TOOL_FILE_UPLOAD_METADATA = ToolMetadata(
"file_upload",
"Upload files to the configured storage backend (local, IPFS, Arweave, etc.)"
)

const TOOL_FILE_UPLOAD_SPECIFICATION = ToolSpecification(
tool_file_upload,
ToolFileUploadConfig,
TOOL_FILE_UPLOAD_METADATA
)
Loading