diff --git a/README.md b/README.md index 1af2f782..13cfc4c3 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ bundle add openai # Anthropic bundle add anthropic +# Atlas Cloud (uses OpenAI-compatible API) +bundle add openai + # Ollama (uses OpenAI-compatible API) bundle add openai @@ -119,6 +122,11 @@ development: access_token: <%= Rails.application.credentials.dig(:anthropic, :access_token) %> model: "claude-sonnet-4.5" + atlas_cloud: + service: "AtlasCloud" + api_key: <%= ENV["ATLASCLOUD_API_KEY"] %> + model: "qwen/qwen3.8-max" + ollama: service: "Ollama" model: "llama3.2" @@ -156,7 +164,7 @@ free low-volume trial. ## Features - **Agent-Oriented Programming**: Build AI applications using familiar Rails patterns -- **Multiple Provider Support**: Works with OpenAI, Anthropic, Ollama, RubyLLM, and more +- **Multiple Provider Support**: Works with OpenAI, Anthropic, Atlas Cloud, Ollama, RubyLLM, and more - **Action-Based Design**: Define agent capabilities through actions - **View Templates**: Use ERB templates for prompts (text, JSON, HTML) - **Streaming Support**: Real-time response streaming with ActionCable diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index cf41e49a..f1ff6e9d 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -130,6 +130,7 @@ export default defineConfig({ text: 'Providers', items: [ { text: 'Anthropic', link: '/providers/anthropic' }, + { text: 'Atlas Cloud', link: '/providers/atlas_cloud' }, { text: 'Ollama', link: '/providers/ollama' }, { text: 'OpenAI', link: '/providers/open_ai' }, { text: 'OpenRouter', link: '/providers/open_router' }, diff --git a/docs/providers.md b/docs/providers.md index 6cd69b6d..40624e95 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -12,6 +12,8 @@ Providers connect your agents to AI services through a unified interface. Switch <<< @/../test/dummy/app/agents/providers/anthropic_agent.rb#agent{ruby} [Anthropic] +<<< @/../test/dummy/app/agents/providers/atlas_cloud_agent.rb#agent{ruby} [Atlas Cloud] + <<< @/../test/dummy/app/agents/providers/ollama_agent.rb#agent{ruby} [Ollama] <<< @/../test/dummy/app/agents/providers/open_ai_agent.rb#agent{ruby} [OpenAI] @@ -33,6 +35,11 @@ end ## Choosing a Provider +### [Atlas Cloud](/providers/atlas_cloud) +**Best for:** OpenAI-compatible access to Atlas Cloud text models + +Use Atlas Cloud model identifiers with Active Agent's chat, streaming, structured output, and tool-calling interfaces. The provider reads `ATLASCLOUD_API_KEY` and uses the Atlas Cloud endpoint automatically. + ### [Anthropic](/providers/anthropic) **Best for:** Complex reasoning, coding tasks, long context diff --git a/docs/providers/atlas_cloud.md b/docs/providers/atlas_cloud.md new file mode 100644 index 00000000..465f6f37 --- /dev/null +++ b/docs/providers/atlas_cloud.md @@ -0,0 +1,50 @@ +--- +title: Atlas Cloud Provider +description: Use Atlas Cloud text models through its OpenAI-compatible Chat Completions API. +--- +# {{ $frontmatter.title }} + +The Atlas Cloud provider connects Active Agent to Atlas Cloud's OpenAI-compatible Chat Completions API. It supports the standard Active Agent chat features, including streaming, structured output, and tool calling when the selected model supports them. + +## Installation + +Atlas Cloud uses the `openai` gem: + +```bash +bundle add openai +``` + +## Configuration + +Set the API key in your environment: + +```bash +ATLASCLOUD_API_KEY=your-api-key +``` + +Configure the provider in `config/active_agent.yml`: + +```yaml +development: + atlas_cloud: + service: "AtlasCloud" + api_key: <%= ENV["ATLASCLOUD_API_KEY"] %> + model: "qwen/qwen3.8-max" +``` + +Then select it from an agent: + +<<< @/../test/dummy/app/agents/providers/atlas_cloud_agent.rb#agent{ruby} + +The provider defaults to `https://api.atlascloud.ai/v1`. You can override `base_url` in the provider configuration when routing through a compatible proxy. + +## Model Selection + +Atlas Cloud model identifiers use a `provider/model` format. Query the current catalog before selecting a model: + +```bash +curl https://api.atlascloud.ai/v1/models \ + -H "Authorization: Bearer $ATLASCLOUD_API_KEY" +``` + +Use the returned model ID as the `model` value. Model capabilities vary, so verify streaming, structured output, or tool support for the chosen model. diff --git a/lib/active_agent/providers/atlas_cloud/options.rb b/lib/active_agent/providers/atlas_cloud/options.rb new file mode 100644 index 00000000..2cd62646 --- /dev/null +++ b/lib/active_agent/providers/atlas_cloud/options.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require_relative "../open_ai/options" + +module ActiveAgent + module Providers + module AtlasCloud + # Configuration options for the Atlas Cloud provider. + class Options < ActiveAgent::Providers::OpenAI::Options + attribute :base_url, :string, as: "https://api.atlascloud.ai/v1" + + private + + def resolve_api_key(kwargs) + kwargs[:api_key] || + kwargs[:access_token] || + ENV["ATLASCLOUD_API_KEY"] + end + + def resolve_organization_id(_kwargs) = nil + def resolve_project_id(_kwargs) = nil + end + end + end +end diff --git a/lib/active_agent/providers/atlas_cloud_provider.rb b/lib/active_agent/providers/atlas_cloud_provider.rb new file mode 100644 index 00000000..42fd4a38 --- /dev/null +++ b/lib/active_agent/providers/atlas_cloud_provider.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative "_base_provider" + +require_gem!(:openai, __FILE__) + +require_relative "open_ai_provider" +require_relative "atlas_cloud/options" + +module ActiveAgent + module Providers + # Provides access to Atlas Cloud's OpenAI-compatible Chat Completions API. + # + # Atlas Cloud uses provider/model identifiers such as +qwen/qwen3.8-max+. + # Request and response handling is shared with the OpenAI Chat provider; + # only the endpoint and API-key resolution are provider-specific. + class AtlasCloudProvider < OpenAI::ChatProvider + # @return [String] + def self.service_name + "AtlasCloud" + end + + # @return [Class] + def self.options_klass + AtlasCloud::Options + end + + # @return [ActiveModel::Type::Value] + def self.prompt_request_type + OpenAI::Chat::RequestType.new + end + + protected + + # @see BaseProvider#api_response_normalize + # @param api_response [OpenAI::Models::ChatCompletion] + # @return [Hash] + def api_response_normalize(api_response) + return api_response unless api_response + + OpenAI::Chat::Transforms.gem_to_hash(api_response) + end + end + end +end diff --git a/test/dummy/app/agents/providers/atlas_cloud_agent.rb b/test/dummy/app/agents/providers/atlas_cloud_agent.rb new file mode 100644 index 00000000..f9c39c50 --- /dev/null +++ b/test/dummy/app/agents/providers/atlas_cloud_agent.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Providers + # region agent + class AtlasCloudAgent < ApplicationAgent + generate_with :atlas_cloud, model: "qwen/qwen3.8-max" + + def ask + prompt(message: params[:message]) + end + end + # endregion agent +end diff --git a/test/providers/atlas_cloud/provider_loading_test.rb b/test/providers/atlas_cloud/provider_loading_test.rb new file mode 100644 index 00000000..739cdf03 --- /dev/null +++ b/test/providers/atlas_cloud/provider_loading_test.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "test_helper" + +class AtlasCloudProviderLoadingTest < ActiveSupport::TestCase + test "loads AtlasCloudProvider via atlas_cloud_provider path" do + require "active_agent/providers/atlas_cloud_provider" + + assert defined?(ActiveAgent::Providers::AtlasCloudProvider) + assert defined?(ActiveAgent::Providers::AtlasCloud::Options) + end + + test "provider concern loads AtlasCloud service correctly" do + provider_class = Class.new(ActiveAgent::Base).provider_load("AtlasCloud") + + assert_equal ActiveAgent::Providers::AtlasCloudProvider, provider_class + end + + test "Atlas Cloud options use the provider endpoint and API key" do + require "active_agent/providers/atlas_cloud_provider" + + options = ActiveAgent::Providers::AtlasCloud::Options.new(api_key: "atlas-test") + + assert_equal "https://api.atlascloud.ai/v1", options.base_url + assert_equal "atlas-test", options.api_key + end + + test "Atlas Cloud options resolve ATLASCLOUD_API_KEY" do + require "active_agent/providers/atlas_cloud_provider" + + previous_key = ENV["ATLASCLOUD_API_KEY"] + ENV["ATLASCLOUD_API_KEY"] = "atlas-env-test" + + options = ActiveAgent::Providers::AtlasCloud::Options.new + + assert_equal "atlas-env-test", options.api_key + ensure + ENV["ATLASCLOUD_API_KEY"] = previous_key + end +end