diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 0df861e2..7fadfe77 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -45,6 +45,8 @@ jobs:
uses: actions/configure-pages@v5
- name: Install dependencies
run: npm ci # or pnpm install / yarn install / bun install
+ - name: Generate doc example output from tests
+ run: bin/test
- name: Build with VitePress
run: npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build
- name: Upload artifact
diff --git a/.gitignore b/.gitignore
index 0626e504..3f9d1f6e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,5 @@
**/.DS_Store
test/dummy/config/master.key
docs/.vitepress/dist
-docs/.vitepress/cache
\ No newline at end of file
+docs/.vitepress/cache
+docs/parts/examples/*.md
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 00000000..c8bc8362
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,224 @@
+# CLAUDE.md - Understanding ActiveAgent Repository
+
+## Overview
+
+ActiveAgent is a Ruby on Rails framework that brings AI-powered capabilities to Rails applications using familiar Rails patterns. It treats AI agents as controllers with enhanced generation capabilities, memory, and tooling.
+
+### Core Concepts
+
+1. **Agents are Controllers** - Agents inherit from `ActiveAgent::Base` and follow Rails controller patterns
+2. **Actions are Tools** - Public methods in agents become tools that can interact with systems and code
+3. **Prompts use Action View** - Leverages Rails' view system for rendering prompts and responses
+4. **Generation Providers** - Common interface for AI providers (OpenAI, Anthropic, Ollama, etc.)
+
+## Repository Structure
+
+```
+activeagent/
+├── lib/active_agent/ # Core framework code
+│ ├── base.rb # Base agent class
+│ ├── generation.rb # Generation logic
+│ ├── action_prompt/ # Prompt system components
+│ └── generation_provider/ # AI provider adapters
+├── test/ # Test suite with examples
+│ ├── dummy/ # Rails test app
+│ └── agents/ # Agent test examples
+├── docs/ # VitePress documentation
+│ ├── docs/ # Markdown documentation files
+│ └── parts/examples/ # Generated example outputs
+└── bin/ # Executable scripts
+```
+
+## Documentation Process
+
+This repository follows a strict documentation process to ensure all code examples are tested and accurate:
+
+### Key Principles
+
+1. **No hardcoded code blocks** - All code must come from tested files
+2. **Use `<<<` imports only** - Import code from actual tested implementation and test files
+3. **Test everything** - If it's in docs, it must have a test
+4. **Include outputs** - Use `doc_example_output` for response examples
+
+### Import Patterns
+
+#### Implementation Files
+```markdown
+<<< @/../test/dummy/app/agents/support_agent.rb {ruby}
+<<< @/../test/dummy/app/agents/support_agent.rb#5-9 {ruby:line-numbers}
+```
+
+#### Test Code with Regions
+
+##### In test file:
+```ruby
+# region unique_region_name
+code_to_include
+# endregion unique_region_name
+```
+##### In docs:
+```markdown
+<<< @/../test/agents/support_agent_test.rb#unique_region_name {ruby:line-numbers}
+```
+
+#### Test Output Examples
+```markdown
+::: details Response Example
+
+:::
+```
+
+### The `doc_example_output` Method
+
+Located in `test/test_helper.rb`, this method:
+- Captures test output and formats it for documentation
+- Generates files in `docs/parts/examples/` with deterministic names
+- Supports Ruby objects, JSON, and response objects
+- Includes metadata (source file, line number, test name)
+
+Usage in tests:
+```ruby
+response = agent.generate(prompt)
+doc_example_output(response) # Generates example file
+```
+
+## Working with Documentation
+
+### Current Branch Status
+The `data-extraction-example-docs` branch is improving documentation found on docs.activeagents.ai. All code snippets should include example outputs using the `doc_example_output` method.
+
+### Documentation Files Needing Review
+
+Files that may still have hardcoded examples:
+- `docs/docs/framework/generation-provider.md`
+- `docs/docs/framework/active-agent.md`
+- `docs/docs/action-prompt/actions.md`
+- `docs/docs/action-prompt/messages.md`
+- `docs/docs/action-prompt/prompts.md`
+
+### Running Tests and Building Docs
+
+1. Run tests to generate examples:
+```bash
+# Run all tests
+bin/test
+
+# Run specific test file
+bin/test test/agents/your_agent_test.rb
+
+# Run specific test by name pattern
+bin/test test/agents/your_agent_test.rb -n "test_name_pattern"
+```
+
+2. Build and serve docs locally:
+```bash
+# Start development server (recommended)
+bin/docs # Starts vitepress dev server at http://localhost:5173
+
+# Build static docs (for production)
+cd docs && npm run docs:build
+
+# Preview built docs
+cd docs && npm run docs:preview
+```
+
+## Key Framework Components
+
+### Agents
+- Inherit from `ActiveAgent::Base`
+- Use `generate_with` to specify AI provider
+- Define actions as public instance methods
+- Support callbacks (`before_action`, `after_generation`)
+
+### Actions
+- Render prompts using `prompt` method
+- Support multiple content types (text, JSON, HTML)
+- Can accept parameters via `with` method
+- Include tool schemas in JSON views
+
+### Generation Providers
+- OpenAI, Anthropic, Ollama, OpenRouter supported
+- Configured in `config/active_agent.yml`
+- Support streaming, callbacks, and queued generation
+
+### Prompts
+- Built using Action View templates
+- Support instructions (default, custom template, or plain text)
+- Include message history and available actions
+- Can be multimodal (text, images, files)
+
+## Testing Conventions
+
+### VCR Cassettes
+- Used for recording API responses
+- Keep existing cassettes committed
+- Create unique names for new tests
+- Ensure `test/dummy/config/master.key` is present
+
+### Test Organization
+- Agent tests in `test/agents/`
+- Framework tests in respective directories
+- Use regions for important test snippets
+- Always call `doc_example_output` for examples
+
+## Important Commands
+
+```bash
+# Install dependencies
+bundle install
+
+# Run all tests
+bin/test
+
+# Run specific test file
+bin/test test/agents/specific_agent_test.rb
+
+# Run specific test by name
+bin/test test/agents/specific_agent_test.rb -n "test_name_pattern"
+
+# Start documentation development server
+bin/docs # http://localhost:5173
+
+# Build documentation for production
+cd docs && npm run docs:build
+
+# Generate new agent
+rails generate active_agent:agent AgentName action1 action2
+```
+
+## Configuration
+
+### active_agent.yml
+```yaml
+development:
+ openai:
+ access_token: <%= Rails.application.credentials.dig(:openai, :api_key) %>
+ model: gpt-4o
+ anthropic:
+ access_token: <%= Rails.application.credentials.dig(:anthropic, :api_key) %>
+ model: claude-3-5-sonnet-latest
+```
+
+### Credentials
+Store API keys in Rails credentials:
+```bash
+rails credentials:edit
+```
+
+## Best Practices
+
+1. **Always test code examples** - Never add untested code to docs
+2. **Use regions in tests** - Makes it easy to import specific snippets
+3. **Include example outputs** - Users need to see what to expect
+4. **Follow Rails conventions** - ActiveAgent extends Rails patterns
+5. **Document tool schemas** - JSON views should clearly define tool structure
+
+## Next Steps for Documentation
+
+When updating documentation:
+1. Find hardcoded examples in markdown files
+2. Create or update tests with proper regions
+3. Add `doc_example_output` calls to generate examples
+4. Replace hardcoded blocks with `<<<` imports
+5. Add `@include` directives for example outputs
+6. Run tests and verify documentation builds correctly
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index 6ed06d21..d8494d4c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- activeagent (0.5.0rc2)
+ activeagent (0.5.0rc3)
actionpack (>= 7.2, < 9.0)
actionview (>= 7.2, < 9.0)
activejob (>= 7.2, < 9.0)
diff --git a/README.md b/README.md
index cb7ad33b..b408918c 100644
--- a/README.md
+++ b/README.md
@@ -15,3 +15,174 @@ Active Agent provides that missing AI layer in the Rails framework, offering a s
## Documentation
[docs.activeagents.ai](https://docs.activeagents.ai) - The official documentation site for Active Agent.
+
+## Getting Started
+
+### Installation
+
+Use bundler to add activeagent to your Gemfile and install:
+```bash
+bundle add activeagent
+```
+
+Add the generation provider gem you want to use:
+
+```bash
+# OpenAI
+bundle add ruby-openai
+
+# Anthropic
+bundle add ruby-anthropic
+
+# Ollama (uses OpenAI-compatible API)
+bundle add ruby-openai
+
+# OpenRouter (uses OpenAI-compatible API)
+bundle add ruby-openai
+```
+
+### Setup
+
+Run the install generator to create the necessary configuration files:
+
+```bash
+rails generate active_agent:install
+```
+
+This creates:
+- `config/active_agent.yml`: Configuration file for generation providers
+- `app/agents`: Directory for your agent classes
+- `app/views/agent_*`: Directory for agent prompt/view templates
+
+### Quick Example
+
+Define an application agent:
+
+```ruby
+class ApplicationAgent < ActiveAgent::Base
+ generate_with :openai,
+ instructions: "You are a helpful assistant.",
+ model: "gpt-4o-mini",
+ temperature: 0.7
+end
+```
+
+Use your agent:
+
+```ruby
+message = "Test Application Agent"
+prompt = ApplicationAgent.with(message: message).prompt_context
+response = prompt.generate_now
+```
+
+### Your First Agent
+
+Generate a new agent:
+
+```bash
+rails generate active_agent:agent TravelAgent search book confirm
+```
+
+This creates an agent with actions that can be called:
+
+```ruby
+class TravelAgent < ApplicationAgent
+ def search
+ # Your search logic here
+ prompt
+ end
+
+ def book
+ # Your booking logic here
+ prompt
+ end
+
+ def confirm
+ # Your confirmation logic here
+ prompt
+ end
+end
+```
+
+## Configuration
+
+Configure generation providers in `config/active_agent.yml`:
+
+```yaml
+development:
+ openai:
+ service: "OpenAI"
+ api_key: <%= Rails.application.credentials.dig(:openai, :api_key) %>
+ model: "gpt-4o-mini"
+ embeddings_model: "text-embedding-3-small"
+
+ anthropic:
+ service: "Anthropic"
+ api_key: <%= Rails.application.credentials.dig(:anthropic, :api_key) %>
+ model: "claude-3-5-sonnet"
+
+ ollama:
+ service: "Ollama"
+ model: "llama3.2"
+ embeddings_model: "nomic-embed-text"
+ host: "http://localhost:11434"
+```
+
+## Features
+
+- **Agent-Oriented Programming**: Build AI applications using familiar Rails patterns
+- **Multiple Provider Support**: Works with OpenAI, Anthropic, Ollama, 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
+- **Tool/Function Calling**: Agents can use tools to interact with external services
+- **Context Management**: Maintain conversation history across interactions
+- **Structured Output**: Define JSON schemas for predictable responses
+
+## Examples
+
+### Data Extraction
+Extract structured data from images, PDFs, and text:
+
+```ruby
+prompt = DataExtractionAgent.with(
+ output_schema: :chart_schema,
+ image_path: Rails.root.join("sales_chart.png")
+).parse_content
+```
+
+### Translation
+Translate text between languages:
+
+```ruby
+response = TranslationAgent.with(
+ message: "Hi, I'm Justin",
+ locale: "japanese"
+).translate.generate_now
+```
+
+### Tool Usage
+Agents can use tools to perform actions:
+
+```ruby
+# Agent with tool support
+message = "Show me a cat"
+prompt = SupportAgent.with(message: message).prompt_context
+response = prompt.generate_now
+# Response includes tool call results
+```
+
+## Learn More
+
+- [Documentation](https://docs.activeagents.ai)
+- [Getting Started Guide](https://docs.activeagents.ai/docs/getting-started)
+- [API Reference](https://docs.activeagents.ai/docs/framework)
+- [Examples](https://docs.activeagents.ai/docs/agents)
+
+## Contributing
+
+We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
+
+## License
+
+Active Agent is released under the [MIT License](LICENSE).
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 5cb66926..60d578f2 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -1,4 +1,5 @@
import { defineConfig } from 'vitepress'
+import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
import {
groupIconMdPlugin,
@@ -10,7 +11,8 @@ import {
export default defineConfig({
markdown: {
config(md) {
- md.use(groupIconMdPlugin)
+ md.use(groupIconMdPlugin),
+ md.use(tabsMarkdownPlugin)
},
},
vite: {
@@ -49,6 +51,10 @@ export default defineConfig({
search: {
provider: 'local',
},
+ editLink: {
+ pattern: 'https://github.com/activeagents/activeagent/edit/main/docs/:path',
+ text: 'Suggest changes to this page on GitHub'
+ },
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
@@ -67,9 +73,9 @@ export default defineConfig({
{
text: 'Framework',
items: [
- { text: 'Generation Provider', link: '/docs/framework/generation-provider' },
{ text: 'Action Prompt', link: '/docs/framework/action-prompt' },
{ text: 'Active Agent', link: '/docs/framework/active-agent' },
+ { text: 'Generation Provider', link: '/docs/framework/generation-provider' },
]
},
{
@@ -78,11 +84,19 @@ export default defineConfig({
{ text: 'Messages', link: '/docs/action-prompt/messages' },
{ text: 'Actions', link: '/docs/action-prompt/actions' },
{ text: 'Prompts', link: '/docs/action-prompt/prompts' },
+ { text: 'Tools', link: '/docs/action-prompt/tools' },
+ ]
+ },
+ { text: 'Agents',
+ items: [
+ { text: 'Data Extraction', link: '/docs/agents/data-extraction-agent' },
+ { text: 'Translation', link: '/docs/agents/translation-agent' },
]
},
{ text: 'Active Agent',
items: [
// { text: 'Generative UI', link: '/docs/active-agent/generative-ui' },
+ { text: 'Structured Output', link: '/docs/agents/data-extraction-agent#structured-output' },
{ text: 'Callbacks', link: '/docs/active-agent/callbacks' },
{ text: 'Generation', link: '/docs/active-agent/generation' },
{ text: 'Queued Generation', link: '/docs/active-agent/queued-generation' },
@@ -97,7 +111,7 @@ export default defineConfig({
{ icon: 'discord', link: 'https://discord.gg/JRUxkkHKmh' },
{ icon: 'linkedin', link: 'https://www.linkedin.com/in/tonsoffun111/' },
{ icon: 'twitch', link: 'https://www.twitch.tv/tonsoffun111' },
- // { icon: 'github', link: 'https://github.com/activeagents/activeagent' }
+ { icon: 'github', link: 'https://github.com/activeagents/activeagent' }
],
}
})
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
index 38e68982..b8867b84 100644
--- a/docs/.vitepress/theme/index.ts
+++ b/docs/.vitepress/theme/index.ts
@@ -5,7 +5,7 @@ import DefaultTheme from 'vitepress/theme'
import './style.css'
import 'virtual:group-icons.css'
import FeatureCards from './components/FeatureCards.vue'
-// import GitHubStars from './components/GitHubStars.vue'
+import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client'
export default {
extends: DefaultTheme,
@@ -18,6 +18,6 @@ export default {
enhanceApp({ app, router, siteData }) {
// Register components globally if needed
app.component('FeatureCards', FeatureCards)
- // app.component('GitHubStars', GitHubStars)
+ enhanceAppWithTabs(app)
}
} satisfies Theme
diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css
index a5889733..18e5a6ec 100644
--- a/docs/.vitepress/theme/style.css
+++ b/docs/.vitepress/theme/style.css
@@ -137,3 +137,48 @@
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}
+.plugin-tabs--tab [data-title]::before{
+ display: inline-block;
+ width: 1em;
+ height: 1em;
+ margin-right: 0.5em;
+ margin-bottom: -0.2em;
+ background: var(--icon) no-repeat center / contain;
+}
+
+.vp-code-block-title-bar {
+ position: relative;
+ margin: 16px -24px 0 -24px;
+ background-color: var(--vp-code-block-bg);
+ overflow-x: auto;
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--vp-code-tab-text-color);
+ white-space: nowrap;
+ transition: background-color 0.5s;
+ border-radius: 8px 8px 0 0;
+ padding:0 12px;
+ box-shadow: inset 0 -1px var(--vp-code-tab-divider);
+}
+
+.custom-block .vp-code-block-title-bar {
+ margin: 16px 0 0 0;
+}
+
+@media (min-width: 640px) {
+ .vp-code-block-title-bar {
+ margin: 16px 0 0 0;
+ }
+}
+
+.vp-code-block-title-text {
+ padding: 0 12px;
+ line-height: 48px;
+}
+
+
+.vp-code-block-title div[class*=language-] {
+ margin-top: 0 !important;
+ border-top-left-radius: 0 !important;
+ border-top-right-radius: 0 !important;
+}
diff --git a/docs/docs/action-prompt/actions.md b/docs/docs/action-prompt/actions.md
index 98ee6f77..7687c9e4 100644
--- a/docs/docs/action-prompt/actions.md
+++ b/docs/docs/action-prompt/actions.md
@@ -1,5 +1,5 @@
# Actions
-Active Agent Actions render views, just like Action Controller or Action Mailer. Similar to Action Mailer that renders Mail objects with Messages, Active Agent renders Prompt objects with Messages.
+Active Agent uses Action View to render Message content for [Prompt](action-prompt/prompts.md) context objects.
## Prompt
The `prompt` method is used to render the action's content as a message in a prompt. The `prompt` method is similar to `mail` in Action Mailer or `render` in Action Controller, it allows you to specify the content type and view template for the action's response.
@@ -17,11 +17,9 @@ You can define actions in your agent class that can be used to interact with the
<<< @/../test/dummy/app/views/translation_agent/translate.text.erb{erb:line-numbers} [translate.text.erb]
:::
-
-
## Set up instructions
-You can configure instructions in several ways when using `generate_with`. Here are the supported options:
+You can configure instructions in several ways when using `generate_with`, as
#### 1. Use the default instructions template
If you don’t pass anything for instructions, it will automatically try to use the default instructions template: `instructions.text.erb`
@@ -60,6 +58,34 @@ These actions can be invoked by the agent to perform specific tasks and receive
## Action params
Agent Actions can accept parameters, which are passed as a hash to the action method. You pass arguments to agent's using the `with` method and access parameters using the `params` method, just like Mailer Actions.
+<<< @/../test/agents/actions_examples_test.rb#actions_with_parameters{ruby:line-numbers}
+
+### Parameters vs Runtime Options
+
+When using the `with` method, it's important to understand the distinction:
+- **Regular parameters** (like `message`, `user_id`, etc.) are accessed via the `params` method in your actions
+- **Runtime options** (like `model`, `temperature`, etc.) should be passed via the `:options` key to configure the generation provider
+
+Example:
+```ruby
+# Regular parameters and runtime options
+TravelAgent.with(
+ destination: "Paris", # Regular parameter
+ user_id: 123, # Regular parameter
+ options: { # Runtime options
+ model: "gpt-4o",
+ temperature: 0.7
+ }
+).search
+
+# In the action, access regular params:
+def search
+ destination = params[:destination] # "Paris"
+ user_id = params[:user_id] # 123
+ # Runtime options are automatically applied to generation
+end
+```
+
## Using Actions to prompt the Agent with a templated message
You can call these actions directly to render a prompt to the agent directly to generate the requested object.
@@ -70,15 +96,16 @@ You can call these actions directly to render a prompt to the agent directly to
## Using Agents to call Actions
You can also provide an Agent with a prompt context that includes actions and messages. The agent can then use these actions to perform tasks and generate responses based on the provided context.
-```ruby
-parameterized_agent = TravelAgent.with(message: "I want to book a hotel in Paris")
-agent_text_prompt_context = parameterized_agent.text_prompt
-
-agent_text_prompt_context.generate_now
-```
+<<< @/../test/agents/actions_examples_test.rb#actions_prompt_context_generation{ruby:line-numbers}
In this example, the `TravelAgent` will use the provided message as context to determine which actions to use during generation. The agent can then call the `search` action to find hotels, `book` action to initialize a hotel booking, or `confirm` action to finalize a booking, as needed based on the prompt context.
+### Content Types
+
+Actions can render different content types based on their purpose:
+
+<<< @/../test/agents/actions_examples_test.rb#actions_content_types{ruby:line-numbers}
+
The `prompt` takes the following options:
- `content_type`: Specifies the type of content to be rendered (e.g., `:text`, `:json`, `:html`).
- `message`: The `message.content` to be displayed in the prompt.
@@ -88,33 +115,44 @@ The `prompt` takes the following options:
* A string with custom instructions (e.g., "Help the user find a hotel");
* A hash referencing a template (e.g., { template: :custom_template });
-```ruby [app/agents/travel_agent.rb]
-class TravelAgent < ActiveAgent::Agent
- def search
- Place.search(params[:location])
- prompt(content_type: :text, template_name: 'search_results', instructions: 'Help the user find a hotel')
- end
-
- def book
- Place.book(hotel_id: params[:hotel_id], user_id: params[:user_id])
- prompt(content_type: :json, template_name: 'booking_confirmation', instructions: { template: 'book_instructions' })
- end
-
- def confirm
- Place.confirm(params[:booking_id])
- prompt(content_type: :html, template_name: 'confirmation_page')
- end
-end
-```
+<<< @/../test/dummy/app/agents/travel_agent.rb {ruby}
## Action View Templates & Partials
While partials can be used in the JSON views the action's json view should primarily define the tool schema, then secondarily define the tool's output using a partial to render results of the tool call all in a single JSON action view template. Use the JSON action views for tool schema definitions and results, and use the text or HTML action views for rendering the action's response to the user.
### Runtime options
-- `content_type`: Specifies the type of content to be rendered (e.g., `:text`, `:json`, `:html`).
-- `view`: Specifies the view template to be used for rendering the action's response. This can be a string representing the view file name or a symbol representing a predefined view.
-- `stream`: If set to `true`, the response will be streamed to the user in real-time. This is useful for long-running actions or when you want to provide immediate feedback to the user.
-- `options`: Additional options that can be passed to the generation provider, such as model parameters or generation settings.
+Runtime options can be passed to agents in several ways:
+
+1. **Via the `with` method** - Pass runtime options using the `:options` parameter:
+<<< @/../test/option_hierarchy_test.rb#runtime_options_with_method{ruby:line-numbers}
+
+2. **In the `prompt` method** - Pass runtime options directly in the prompt call:
+<<< @/../test/option_hierarchy_test.rb#runtime_options_in_prompt{ruby:line-numbers}
+
+3. **Supported runtime option types**:
+<<< @/../test/option_hierarchy_test.rb#runtime_options_types{ruby:line-numbers}
+
+Available runtime options include:
+- `model`: The model to use for generation (e.g., "gpt-4o", "claude-3")
+- `temperature`: Controls randomness in generation (0.0 to 1.0)
+- `max_tokens`: Maximum number of tokens to generate
+- `stream`: If set to `true`, the response will be streamed in real-time
+- `top_p`: Nucleus sampling parameter
+- `frequency_penalty`: Penalizes repeated tokens based on frequency
+- `presence_penalty`: Penalizes repeated tokens based on presence
+- `seed`: For deterministic generation
+- `stop`: Sequences where generation should stop
+- `response_format`: Format for structured outputs
+
+::: details Runtime Options Example
+
+:::
+
+**Option precedence**: When options are specified in multiple places, they follow this hierarchy:
+1. Config options (lowest priority)
+2. Agent-level options (set with `generate_with`)
+3. Explicit options (passed via `:options` parameter)
+4. Runtime options (highest priority)
## How Agents use Actions
1. The agent receives a request from the user, which may include a message or an action to be performed.
diff --git a/docs/docs/action-prompt/messages.md b/docs/docs/action-prompt/messages.md
index 1a24b754..ee915d7b 100644
--- a/docs/docs/action-prompt/messages.md
+++ b/docs/docs/action-prompt/messages.md
@@ -9,28 +9,42 @@ Messages can be structured as a Message object or hash with the following attrib
- `content`: The content of the message, which can be plain text or formatted content.
- `requested_actions`: An array of actions that the agent requested to be performed in response to the message. This is used to handle tool use requests from the agent.
+<<< @/../test/agents/messages_examples_test.rb#messages_structure{ruby:line-numbers}
+
## Instructions as system messages to the agent
-A `:system` message is used to provide instructions to the agent. This message is used to set the context for the generation process and can be used to provide additional information about the interaction to the agent. Instructions can include how an agent should use their available actions to achieve a desired outcome or contain embedded ruby to render retrieve data to augment the generation process with contextual information prior to user-agent interactions.
+A `:system` message is used to provide instructions to the agent. This message is used to set the context for the generation process and can be used to provide additional information about the interaction to the agent. Instructions can include how an agent should use their available tool actions to achieve a desired outcome as well as render embedded ruby representations of retrieved data to augment the generation process with contextual information prior to user-agent interactions.
+
+<<< @/../test/dummy/app/views/travel_agent/instructions.text.erb {erb}
+
+### Agent instructions
+Agent's can use `generate_with` to define instructions for the agent.
+
+<<< @/../test/dummy/app/agents/application_agent.rb {4 ruby:line-numbers}
+
+Agent's can also use implicit instructions by defining an `instructions` view in the agent's view directory.
+
+<<< @/../test/dummy/app/views/application_agent/instructions.text.erb {erb}
-```erb [app/views/agents/instructions.text.erb]
-This agent is currently interacting with <%= @user.name %> to find a hotel near their travel destination.
-The agent should use the following actions to achieve the desired outcome:
-<% actions do |action| %>
- <%= action.name %>: <%= action.description %>
-<% end %>
-requirements:
-- The agent should use the `:search` action to find hotels in the requested location.
-- The agent should use the `:book` action to book a hotel for the user.
-- The agent should use the `:confirm` action to confirm the booking with the user.
-```
## User's send :user messages to the agent
A `:user` message is used to represent the user's input to the agent. These messages are commonly seen as plain text chat messages, but should be thought of as an Action View that could be of any type you choose to support, just like Action Mailer can send 'plain/text' or 'html/text' emails, Action Prompt render formatted messages to the agents.
## Agent's send :assistant messages to the user
-An `:assistant` message is used to represent the agent's response to the user. These messages are also often seen as justin plain text chat messages, but again should be thought of as an Action View template that could be of any type you choose to support. This enables the agent to render formatted messages to the user, such as HTML or TXT with interpolated instance variables and embedded ruby. The agent can use these messages to provide additional information or context to the user, and can also include links or other interactive elements.
+An `:assistant` message is used to represent the agent's response to the user. These messages are also often seen as plain text chat messages, but again should be thought of as an Action View template that could be of any type you choose to support. This enables the agent to render formatted messages to the user, such as HTML or TXT with interpolated instance variables and embedded ruby. The agent can use these messages to provide additional information or context to the user, and can also include links or other interactive elements.
+
+### Messages with Requested Actions
+
+<<< @/../test/agents/messages_examples_test.rb#messages_with_actions{ruby:line-numbers}
## The system responds to agent requested actions with :tool messages
-Agent performed actions result in `:tool` message. These messages are used to represent the response to a tool call made by the agent. This message is used to provide additional information about the tool call, such as the name of the tool and any arguments that were passed to the tool. The system can use this message to provide a response message containing the result of the tool call and can also include links or other interactive elements.
\ No newline at end of file
+Agent performed actions result in `:tool` message. These messages are used to represent the response to a tool call made by the agent. This message is used to provide additional information about the tool call, such as the name of the tool and any arguments that were passed to the tool. The system can use this message to provide a response message containing the result of the tool call and can also include links or other interactive elements.
+
+<<< @/../test/agents/messages_examples_test.rb#tool_messages{ruby:line-numbers}
+
+## Building Message Context
+
+Messages form the conversation history that provides context for the agent:
+
+<<< @/../test/agents/messages_examples_test.rb#message_context{ruby:line-numbers}
\ No newline at end of file
diff --git a/docs/docs/action-prompt/prompts.md b/docs/docs/action-prompt/prompts.md
index a26fe1ea..2b1baf1e 100644
--- a/docs/docs/action-prompt/prompts.md
+++ b/docs/docs/action-prompt/prompts.md
@@ -10,6 +10,7 @@ The Prompt is structured to include the following components:
- **Messages**: An array of messages that represent the contextual information or conversational chat history. Each message includes content, role (user or assistant), and metadata.
- **Message**: The Action Prompt's rendered message object or hash that contains the content and role of the message. The role can be `:user`, `:system`, `:assistant`, or `:tool`, but defaults to `:user`.
- **Actions**: An array of actions that the agent can perform in response to user input. By default, the prompt will use the agent's action methods, but you can also specify custom actions.
+- **Options**: Runtime configuration options that control the generation behavior (e.g., model, temperature, max_tokens). See [Runtime options](actions.md#runtime-options) for details.
## Example Prompt
Prompts are built and rendered in the agent's action methods, typically using the `prompt` method. This is an example of creating a prompt by manually building the context; assigning `actions`, the prompt `message` and context `messages`.
diff --git a/docs/docs/action-prompt/tools.md b/docs/docs/action-prompt/tools.md
new file mode 100644
index 00000000..e807d912
--- /dev/null
+++ b/docs/docs/action-prompt/tools.md
@@ -0,0 +1,50 @@
+---
+title: Tools and Actions
+---
+# {{ $frontmatter.title }}
+
+Active Agent supports tool/function calling, allowing agents to interact with external services and perform actions.
+
+## Tool Support
+
+Agents can define and use tools to extend their capabilities:
+
+<<< @/../test/dummy/app/agents/support_agent.rb {ruby}
+
+## Tool Usage Example
+
+Here's how an agent uses tools to fulfill user requests:
+
+<<< @/../test/agents/support_agent_test.rb#support_agent_tool_call {ruby:line-numbers}
+
+### Tool Call Response
+
+When a tool is called, the response includes the tool's output in the conversation:
+
+<<< @/../test/agents/support_agent_test.rb#support_agent_tool_call_response {ruby:line-numbers}
+
+::: details Response Example
+
+:::
+
+## Tool Response Structure
+
+When tools are used, the response includes:
+- **System Message**: Initial instructions for the agent
+- **User Message**: The original user request
+- **Assistant Message**: The agent's decision to use a tool
+- **Tool Message**: The result from the tool execution
+
+The final response contains 4 messages showing the complete tool interaction flow.
+
+## Implementing Tools
+
+Tools are defined as methods in your agent class. The tool's JSON schema is defined in the corresponding view template:
+
+### Tool Implementation
+
+<<< @/../test/dummy/app/agents/support_agent.rb#5-9 {ruby:line-numbers}
+
+### Tool Schema Definition
+
+<<< @/../test/dummy/app/views/support_agent/get_cat_image.json.erb {erb}
\ No newline at end of file
diff --git a/docs/docs/active-agent/callbacks.md b/docs/docs/active-agent/callbacks.md
index 0b0f1a3e..33375dea 100644
--- a/docs/docs/active-agent/callbacks.md
+++ b/docs/docs/active-agent/callbacks.md
@@ -5,40 +5,26 @@ Callbacks can be registered to execute on specific events during the prompt and
## Action Callbacks
Action callbacks are triggered when an action is invoked within an agent. This allows you to customize the behavior of actions, such as modifying the action's parameters or logging the action's execution. Great for retrieval augmented generation (RAG) workflows.
-```ruby
-class ApplicationAgent < ActiveAgent::Base
- generate_with :openai
-
- before_action :set_context
-
- private
- def set_context
- # Logic to set the context for the action, e.g., setting a user ID or session data
- @context = Context.find(params[:context_id])
- prompt_context.messages = @context.messages
- end
-end
-```
+<<< @/../test/agents/callback_agent_test.rb#callback_agent_before_action {ruby:line-numbers}
## Generation Callbacks
Generation callbacks are executed during the generation process of an agent. This allows you to modify the prompt, handle responses, or perform additional processing based on the generated content.
-```ruby
-class ApplicationAgent < ActiveAgent::Base
- generate_with :openai
-
- after_generation :process_response
-
- private
- def process_response
- generation_provider.response
- end
-end
-```
+<<< @/../test/agents/callback_agent_test.rb#callback_agent_after_generation {ruby:line-numbers}
## On Stream Callbacks
On stream callbacks are triggered during the streaming of responses from an agent. This allows you to handle real-time updates, such as displaying partial responses in a user interface or logging the progress of the response generation.
-Below is a verbose example to demonstrate how to handle streaming responses and broadcast updates to a chat interface. The shows the runtime instance of the Agent's [`generation_provider`](/docs/framework/generation-provider) and its [`response`](/docs/framework/generation-provider#response) object. This example assumes you have a `Chat` model with associated messages, and you want to update the chat in real-time as the agent generates a response.
+### Streaming Implementation
+
+The streaming agent demonstrates real-time response handling:
+
+<<< @/../test/dummy/app/agents/streaming_agent.rb{ruby:line-numbers}
+
+### Testing Streaming
+
+The streaming functionality broadcasts each chunk as it arrives:
+
+<<< @/../test/agents/streaming_agent_test.rb#streaming_agent_stream_response {ruby:line-numbers}
-<<< @/../test/dummy/app/agents/streaming_agent.rb{ruby:line-numbers} [streaming_agent.rb]
+In this test, the agent broadcasts 30 chunks during the streaming response.
diff --git a/docs/docs/active-agent/generation.md b/docs/docs/active-agent/generation.md
index 2be9e7eb..a077c70c 100644
--- a/docs/docs/active-agent/generation.md
+++ b/docs/docs/active-agent/generation.md
@@ -5,13 +5,20 @@ Prompt Generation is a core feature of the Active Agent framework, enabling the
## Generate now
To generate a prompt immediately, you can use the `generate_now` method. This method processes the prompt context and returns the generated response synchronously.
-<<< @/../test/agents/application_agent_test.rb#application_agent_prompt_context_message_generation{ruby}
+<<< @/../test/agents/application_agent_test.rb#application_agent_prompt_context_message_generation{ruby:line-numbers}
+::: details Response Example
+
+:::
### Generation with Context
Loading a context from an existing prompt context:
-<<< @/../test/agents/application_agent_test.rb#application_agent_loaded_context_message_generation{ruby}
+<<< @/../test/agents/application_agent_test.rb#application_agent_loaded_context_message_generation{ruby:line-numbers}
+
+::: details Response Example
+
+:::
## Key Features
diff --git a/docs/docs/active-agent/queued-generation.md b/docs/docs/active-agent/queued-generation.md
index 6d004d50..3bd0a33e 100644
--- a/docs/docs/active-agent/queued-generation.md
+++ b/docs/docs/active-agent/queued-generation.md
@@ -5,9 +5,13 @@ Queued generation allows you to handle prompt generation asynchronously, which i
Generation can be performed using Active Job to handle the prompt-generation and perform actions asynchronously. This is the most common way to handle generation in production applications, as it allows for better scalability and responsiveness.
-To perform queued generation, you can use the `generate_later` method, which enqueues the generation job to be processed later by Active Job.
+To perform queued generation, you can use the `generate_later` method, which enqueues the generation job to be processed later by Active Job.
-```ruby
-ApplicationAgent.with(message: params[:message], messages: params[:messages]).text_prompt.generate_later
-```
+<<< @/../test/agents/queued_generation_test.rb#queued_generation_generate_later {ruby:line-numbers}
+
+## Custom Queue Configuration
+
+You can specify custom queue names and priorities:
+
+<<< @/../test/agents/queued_generation_test.rb#queued_generation_custom_queue {ruby:line-numbers}
diff --git a/docs/docs/active-agent/travel-agent.md b/docs/docs/active-agent/travel-agent.md
index 2e155641..2779d0b7 100644
--- a/docs/docs/active-agent/travel-agent.md
+++ b/docs/docs/active-agent/travel-agent.md
@@ -1,43 +1,133 @@
-```json [app/views/agents/travel_agent/search.json.erb]
-{
- "tool": {
- "name": "search",
- "description": "Search for travel options",
- "parameters": {
- "type": "object",
- "properties": {
- "location": {
- "type": "string",
- "description": "The location to search for travel options"
- }
- },
- "required": ["location"]
- }
- }
-}
-```
-
-```erb [app/views/agents/travel_agent/search.html.erb]
-
Search for Travel Options
-Enter the location you want to search for:
-
-```
+# TravelAgent Example
+
+The TravelAgent demonstrates how to build an AI agent that can interact with users through natural language and perform specific actions via tool calling. This agent showcases the ActiveAgent framework's ability to:
+
+- Define actions that can be called by an LLM as tools
+- Handle different response formats (HTML, text, JSON)
+- Process parameters passed from the LLM to controller actions
+- Maintain conversation context across multiple tool calls
+
+## Agent Definition
+
+<<< @/../test/dummy/app/agents/travel_agent.rb {ruby}
+
+## Tool Schemas
+
+Each action is automatically exposed as a tool to the LLM through JSON schemas. The schemas define the parameters that the LLM should provide when calling each tool.
+
+### Search Action Schema
+
+<<< @/../test/dummy/app/views/travel_agent/search.json.jbuilder {ruby}
+
+### Book Action Schema
+
+<<< @/../test/dummy/app/views/travel_agent/book.json.jbuilder {ruby}
+
+### Confirm Action Schema
+
+<<< @/../test/dummy/app/views/travel_agent/confirm.json.jbuilder {ruby}
+
+## View Templates
+
+Each action has corresponding view templates that format the response:
+
+### search.html.erb
+
+<<< @/../test/dummy/app/views/travel_agent/search.html.erb {erb}
+
+### book.text.erb
+
+<<< @/../test/dummy/app/views/travel_agent/book.text.erb {erb}
+
+### confirm.text.erb
+
+<<< @/../test/dummy/app/views/travel_agent/confirm.text.erb {erb}
+
+## Usage Examples
+
+### Basic LLM Interaction
+
+When you create a prompt context and generate a response, the LLM can call the TravelAgent's tools:
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_search_llm {ruby:line-numbers}
+
+::: details Response Example
+
+:::
+
+### Booking a Flight
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_book_llm {ruby:line-numbers}
+
+::: details Response Example
+
+:::
+
+### Confirming a Booking
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_confirm_llm {ruby:line-numbers}
+
+::: details Response Example
+
+:::
+
+### Full Conversation Flow
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_conversation_flow {ruby:line-numbers}
+
+::: details Response Example
+
:::
-This code snippet defines the `TravelAgent` class with three actions: `search`, `book`, and `confirm`. Each action can be implemented with specific logic to handle travel-related queries. The `prompt` method is used to render the action's content in the prompt context.
+### Direct Action Invocation
+
+You can also call actions directly with parameters:
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_search_html {ruby:line-numbers}
+
+::: details Response Example
+
+:::
+
+## How Tool Calling Works
+
+### Tool Call Structure
+
+<<< @/../test/agents/travel_agent_tool_call_test.rb#4-14 {ruby:line-numbers}
+
+### Parameter Processing
+
+<<< @/../test/agents/travel_agent_tool_call_test.rb#52-90 {ruby:line-numbers}
+
+The framework automatically:
+
+1. **Schema Discovery**: Discovers all public instance methods (actions) and loads their JSON schemas
+2. **LLM Tool Selection**: The LLM receives available tools and decides which to call
+3. **Parameter Passing**: Tool parameters are set on the controller's `params` hash
+4. **Action Execution**: The framework executes the action and updates context
+5. **Response Generation**: The action renders its view template
+
+## Multi-Format Support
+
+The TravelAgent demonstrates how different actions can use different response formats:
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_multi_format {ruby:line-numbers}
+
+- **Search**: Uses HTML format for rich UI display
+- **Book**: Uses text format for simple confirmation messages
+- **Confirm**: Uses text format for final booking confirmation
+
+## Testing with VCR
+
+The TravelAgent tests use VCR to record actual LLM interactions. The cassettes capture real OpenAI API calls showing how the LLM:
+- Receives the tool schemas
+- Decides which action to call
+- Passes the appropriate parameters
-## Basic Usage
-When interfacing with an agent, you typically start by providing a prompt context to the agent. This context can include instructions, user messages, and any other relevant information that the agent needs to generate a response. The agent will then process this context and return a response based on its defined actions.
+## Best Practices
-```ruby
-TravelAgent.with(
- instructions: "Help users with travel-related queries, using the search, book, and confirm actions.",
- messages: [
- { role: 'user', content: 'I need a hotel in Paris' }
- ]
-).prompt_context.generate_later
-```
-This code snippet initializes the `TravelAgent` with a set of instructions and a user message. The agent will then process this context and generate a response based on its defined actions. -->
+1. **Parameter Validation**: Always validate and sanitize parameters received from the LLM
+2. **Error Handling**: Provide helpful error messages when required parameters are missing
+3. **Idempotency**: Design actions to be safely repeatable
+4. **Context Preservation**: The framework automatically maintains conversation context across tool calls
+5. **Testing**: Use VCR to record and replay LLM interactions for consistent testing
\ No newline at end of file
diff --git a/docs/docs/agents/data-extraction-agent.md b/docs/docs/agents/data-extraction-agent.md
index b55b23ac..ba9a1749 100644
--- a/docs/docs/agents/data-extraction-agent.md
+++ b/docs/docs/agents/data-extraction-agent.md
@@ -2,13 +2,110 @@
title: Data Extraction Agent
---
# {{ $frontmatter.title }}
-Active Agent is designed to allow developers to create agents with ease. This guide will help you set up a Data Extraction Agent that can extract structured data from unstructured text, images, or PDFs.
-## Creating the Data Extraction Agent
-To create a Data Extraction Agent, you can use the `rails generate active_agent:agent data_extraction extract` command. This will create a new agent class in `app/agents/data_extraction_agent.rb` and a corresponding view template in `app/views/data_extraction_agent/extract.text.erb`.
+Active Agent provides data extraction capabilities to parse structured data from unstructured text, images, or PDFs.
+
+## Setup
+
+Generate a data extraction agent:
```bash
-rails generate active_agent:agent data_extraction extract
+rails generate active_agent:agent data_extraction parse_content
```
+## Agent Implementation
+
+::: code-group
+
<<< @/../test/dummy/app/agents/data_extraction_agent.rb {ruby}
+
+<<< @/../test/dummy/app/views/data_extraction_agent/chart_schema.json.erb {json}
+
+<<< @/../test/dummy/app/views/data_extraction_agent/resume_schema.json.erb {json}
+
+:::
+
+## Basic Image Example
+
+### Image Description
+
+Active Agent can extract descriptions from images without structured output:
+
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_describe_cat_image {ruby:line-numbers}
+
+::: details Basic Cat Image Response Example
+
+:::
+
+### Image: Parse Chart Data
+
+Active Agent can extract data from chart images:
+
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_chart {ruby:line-numbers}
+
+::: details Basic Chart Image Response Example
+
+:::
+
+## Structured Output
+Active Agent supports structured output using JSON schemas. Define schemas in your agent's views directory (e.g., `app/views/data_extraction_agent/`) and reference them using the `output_schema` parameter.
+
+### Structured Output Schemas
+
+When using structured output:
+- The response will have `content_type` of `application/json`
+- The response content will be valid JSON matching your schema
+- Parse the response with `JSON.parse(response.message.content)`
+
+
+### Parse Chart Image with Structured Output
+
+
+Extract chart data with a predefined schema `chart_schema`:
+::: code-group
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_chart_with_structured_output {ruby:line-numbers}
+
+<<< @/../test/dummy/app/views/data_extraction_agent/chart_schema.json.erb {json}
+:::
+
+#### Response
+
+:::: tabs
+
+== Response Object
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_chart_with_structured_output_response {ruby}
+::: details Generation Response Example
+
+:::
+== JSON Output
+
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_chart_with_structured_output_json {ruby}
+::: details Parse Chart JSON Response Example
+
+:::
+::::
+
+### Parse Resume
+
+Extract information from PDF resumes:
+
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_resume {ruby:line-numbers}
+
+### Parse Resume with Structured Output
+
+Extract resume data with a predefined schema:
+
+:::: tabs
+
+== Prompt Generation
+
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_resume_with_structured_output_response {ruby:line-numbers}
+
+
+== JSON Output
+
+<<< @/../test/agents/data_extraction_agent_test.rb#data_extraction_agent_parse_resume_with_structured_output_json {ruby:line-numbers}
+
+
+:::
+::::
\ No newline at end of file
diff --git a/docs/docs/agents/translation-agent.md b/docs/docs/agents/translation-agent.md
new file mode 100644
index 00000000..0d8ea0e2
--- /dev/null
+++ b/docs/docs/agents/translation-agent.md
@@ -0,0 +1,49 @@
+---
+title: Translation Agent
+---
+# {{ $frontmatter.title }}
+
+The Translation Agent demonstrates how to create specialized agents for specific tasks like language translation.
+
+## Setup
+
+Generate a translation agent:
+
+```bash
+rails generate active_agent:agent translation translate
+```
+
+## Implementation
+
+<<< @/../test/dummy/app/agents/translation_agent.rb {ruby}
+
+## Usage Examples
+
+### Basic Translation
+
+The translation agent accepts a message and target locale:
+
+<<< @/../test/agents/translation_agent_test.rb#translation_agent_render_translate_prompt {ruby:line-numbers}
+
+### Translation Generation
+
+Generate a translation using the configured AI provider:
+
+<<< @/../test/agents/translation_agent_test.rb#translation_agent_translate_prompt_generation {ruby:line-numbers}
+
+::: details Response Example
+
+:::
+
+## Key Features
+
+- **Action-based Translation**: Use the `translate` action to process translations
+- **Locale Support**: Pass target language as a parameter
+- **Prompt Templates**: Customize translation prompts through view templates
+- **Instruction Override**: Define custom translation instructions per agent
+
+## View Templates
+
+The translation agent uses view templates to format prompts:
+
+<<< @/../test/dummy/app/views/translation_agent/translate.text.erb {erb}
\ No newline at end of file
diff --git a/docs/docs/documentation-process.md b/docs/docs/documentation-process.md
new file mode 100644
index 00000000..9216adec
--- /dev/null
+++ b/docs/docs/documentation-process.md
@@ -0,0 +1,152 @@
+# Documentation Process Guide
+
+## Prompt for New Session
+
+```
+Please help me continue improving the ActiveAgent documentation by ensuring all code examples come from tested code using the process documented in this file. The goal is to have deterministic documentation that uses only tested code snippets and includes example outputs from tests. Review this documentation-process.md file and continue where we left off, following the established patterns.
+```
+
+## Process Overview
+
+### 1. Enhanced Test Helper Method
+
+We created an enhanced `doc_example_output` method in `test/test_helper.rb` that:
+- Captures test output and formats it for documentation
+- Includes metadata (source file, line number, test name)
+- Supports both Ruby objects and JSON formatting
+- Generates files in `docs/parts/examples/` with deterministic names
+
+```ruby
+def doc_example_output(example = nil, test_name = nil)
+ # Extracts caller info and formats output with metadata
+ # Outputs to: docs/parts/examples/{file_name}-{test_name}.md
+end
+```
+
+### 2. Documentation Patterns
+
+#### For Implementation Files (app/agents/*)
+- Use full file imports: `<<< @/../test/dummy/app/agents/support_agent.rb {ruby}`
+- Use line ranges when needed: `<<< @/../test/dummy/app/agents/support_agent.rb#5-9 {ruby:line-numbers}`
+- NO regions needed for implementation files
+
+#### For Test Code Snippets
+- Add regions around important test code:
+```ruby
+# region unique_region_name
+code_to_include
+# endregion unique_region_name
+```
+- Import with: `<<< @/../test/agents/support_agent_test.rb#support_agent_tool_call {ruby:line-numbers}`
+
+#### For Test Output Examples
+- Call `doc_example_output(response)` in tests to generate example files
+- Include in docs with:
+```markdown
+::: details Response Example
+
+:::
+```
+
+### 3. Key Files Created/Modified
+
+#### Test Files with Regions
+- `test/agents/data_extraction_agent_test.rb` - Added regions and doc_example_output calls
+- `test/agents/translation_agent_test.rb` - Added regions and doc_example_output calls
+- `test/agents/support_agent_test.rb` - Added regions for tool usage examples
+- `test/agents/application_agent_test.rb` - Added doc_example_output calls
+- `test/agents/streaming_agent_test.rb` - Added regions for streaming examples
+- `test/agents/callback_agent_test.rb` - Created new test for callback examples
+- `test/agents/queued_generation_test.rb` - Created new test for queued generation
+- `test/configuration_examples_test.rb` - Created for configuration documentation
+- `test/generation_provider_examples_test.rb` - Created for provider examples
+- `test/tool_configuration_test.rb` - Created for tool configuration examples
+
+#### Enhanced Framework Files
+- `lib/active_agent/generation_provider/response.rb` - Added `usage` method and token helpers
+
+#### Updated Documentation
+- `docs/docs/agents/data-extraction-agent.md` - Uses test regions, includes tabbed response examples
+- `docs/docs/agents/translation-agent.md` - Created with test examples
+- `docs/docs/action-prompt/tools.md` - Updated to use real implementation files
+- `docs/docs/active-agent/generation.md` - Added response examples
+- `docs/docs/active-agent/callbacks.md` - Updated to use test regions
+- `docs/docs/active-agent/queued-generation.md` - Updated to use test regions
+- `docs/docs/getting-started.md` - Removed hardcoded examples, uses real files
+
+### 4. Documentation Rules
+
+1. **NO hardcoded code blocks** - All code must come from tested files
+2. **Use `<<<` imports only** - No manual code blocks
+3. **Test everything** - If it's in docs, it must have a test
+4. **Include outputs** - Use doc_example_output for response examples
+5. **Line numbers** - Use `{ruby:line-numbers}` for test snippets
+
+### 5. Remaining Tasks
+
+From the todo list:
+- [x] Create test for callbacks (before_action, after_generation)
+- [x] Create test for queued generation (generate_later)
+- [x] Create configuration test examples
+- [x] Update getting-started.md to use test examples
+- [x] Update callbacks.md to use test examples
+- [ ] Update other docs to remove hardcoded examples
+
+Files still needing review:
+- `docs/docs/framework/generation-provider.md` - Has some hardcoded provider examples
+- `docs/docs/framework/active-agent.md` - May have hardcoded examples
+- `docs/docs/action-prompt/actions.md` - May have hardcoded examples
+- `docs/docs/action-prompt/messages.md` - Needs review
+- `docs/docs/action-prompt/prompts.md` - Needs review
+
+### 6. Testing Process
+
+1. Run new tests to generate VCR cassettes and example outputs:
+ ```bash
+ bin/test test/agents/callback_agent_test.rb
+ bin/test test/agents/queued_generation_test.rb
+ # etc...
+ ```
+
+2. Verify example outputs are generated in `docs/parts/examples/`
+
+3. Build docs locally to verify imports work:
+ ```bash
+ bin/docs # Starts vitepress dev server
+ ```
+
+### 7. VCR Cassette Management
+
+- Keep existing cassettes that are committed
+- Create new cassettes with unique names for new tests
+- If API errors occur, ensure `test/dummy/config/master.key` is present
+- Use descriptive cassette names that match the test purpose
+
+### 8. Example Patterns
+
+#### Simple Test Import
+```markdown
+<<< @/../test/agents/application_agent_test.rb#application_agent_prompt_context_message_generation{ruby:line-numbers}
+```
+
+#### Response Example with Tabs
+```markdown
+::: tabs
+
+== Response Object
+
+
+
+== JSON Output
+
+
+
+:::
+```
+
+#### Implementation File Import
+```markdown
+<<< @/../test/dummy/app/agents/support_agent.rb {ruby}
+```
+
+This process ensures all documentation is accurate, tested, and maintainable.
\ No newline at end of file
diff --git a/docs/docs/framework/action-prompt.md b/docs/docs/framework/action-prompt.md
index e5ef9b62..4c583fdc 100644
--- a/docs/docs/framework/action-prompt.md
+++ b/docs/docs/framework/action-prompt.md
@@ -5,6 +5,10 @@ title: Action Prompt
Action Prompt provides a structured way to manage prompt contexts and handle responses with callbacks as well as perform actions that render messages using Action View templates.
+ActiveAgent::Base implements a `prompt_context` action which by default will render a prompt context object with message text content, that can contain messages, actions, and params. This action doesn't need a template as long as a Message or String are passed into the `message: params[:message]`
+
+<<< @/../test/agents/application_agent_test.rb#application_agent_prompt_context_message_generation {ruby:line-numbers}
+
Similarly to Action Mailers that render mail messages that are delivered through configured delivery methods, Action Prompt integrates with Generation Providers through the generation module. This allows for dynamic content generation and the ability to use Rails helpers and partials within the prompt templates as well as rendering content from performed actions. Empowering developers with a powerful way to create interactive and engaging user experiences.
## Prompt-generation Request-Response Cycle
diff --git a/docs/docs/framework/active-agent.md b/docs/docs/framework/active-agent.md
index 0dac453d..e509f31d 100644
--- a/docs/docs/framework/active-agent.md
+++ b/docs/docs/framework/active-agent.md
@@ -40,49 +40,31 @@ The primary action is the `prompt_context` which provides a common interface to
- **[Streaming](/docs/active-agent/callbacks#on-stream-callbacks)**: Support real-time updates with the `on_stream` callback to the user interface based on agent interactions.
## Example
+
::: code-group
-```ruby
-class TravelAgent < ApplicationAgent
- def search
- prompt(message: params[:message], content_type: :html)
- end
-
- def book
- prompt(message: params[:message], content_type: :json)
- end
-
- def confirm
- prompt(message: params[:message], content_type: :text)
- end
-end
-```
-
-```html
-<%# app/views/travel_agent/search.html.erb %>
-
-
Search Results
-
<%= @prompt.message %>
-
- <% @prompt.results.each do |result| %>
- - <%= result.title %> - <%= result.price %>
- <% end %>
-
-
-```
-
-```erb [json]
-{
- "message": "<%= @prompt.message %>",
- "results": <%= @prompt.results.to_json %>
-}
-```
-
-```erb [text]
-<%# app/views/travel_agent/confirm.text.erb %>
-Your booking has been confirmed!
-Booking ID: <%= @prompt.booking_id %>
-Thank you for choosing our service.
-```
+<<< @/../test/dummy/app/agents/travel_agent.rb {ruby} [travel_agent.rb]
+
+<<< @/../test/dummy/app/views/travel_agent/search.html.erb {erb} [search.html.erb]
+
+<<< @/../test/dummy/app/views/travel_agent/book.text.erb {erb} [book.text.erb]
+
+<<< @/../test/dummy/app/views/travel_agent/confirm.text.erb {erb} [confirm.text.erb]
+:::
+
+### Using the Travel Agent
+
+<<< @/../test/agents/travel_agent_test.rb#travel_agent_multi_format{ruby:line-numbers}
+
+::: details Search Response Example
+
+:::
+
+::: details Book Response Example
+
+:::
+
+::: details Confirm Response Example
+
:::
## Concepts
diff --git a/docs/docs/framework/generation-provider.md b/docs/docs/framework/generation-provider.md
index e2b79bb6..92211edf 100644
--- a/docs/docs/framework/generation-provider.md
+++ b/docs/docs/framework/generation-provider.md
@@ -8,17 +8,7 @@ You can use the following generation providers with Active Agent:
<<< @/../test/dummy/app/agents/open_ai_agent.rb#snippet{ruby:line-numbers} [OpenAI]
-```ruby [Anthropic]
-class ApplicationAgent < ActiveAgent::Base
- generate_with :anthropic
-end
-```
-
-```ruby [Google]
-class ApplicationAgent < ActiveAgent::Base
- generate_with :google
-end
-```
+<<< @/../test/dummy/app/agents/anthropic_agent.rb {ruby} [Anthropic]
<<< @/../test/dummy/app/agents/open_router_agent.rb#snippet{ruby:line-numbers} [OpenRouter]
@@ -38,22 +28,27 @@ The `ActiveAgent::GenerationProvider::Response` class encapsulates the result of
- **`raw_response`** - The unprocessed response data from the AI provider, useful for debugging and accessing provider-specific metadata
#### Example Usage
-::: code-group
-<<<@/../test/dummy/app/agents/application_agent.rb#application_agent_text_prompt_message_generation{ruby:line-numbers} [application_agent.rb]
+<<< @/../test/generation_provider_examples_test.rb#generation_response_usage{ruby:line-numbers}
+
+::: details Response Example
+
+:::
+The response object ensures you have full visibility into both the input prompt context and the raw provider response, making it easy to debug generation issues or access provider-specific response metadata.
-```ruby [irb]{ruby:line-numbers}
-# Access the response message
-puts response.message
+## Provider Configuration
-# Inspect the prompt that was sent
-puts response.prompt.inspect
+You can configure generation providers with custom settings:
-# Access the messages in the prompt
-puts response.prompt.messages.inspect
+### Model and Temperature Configuration
+
+<<< @/../test/generation_provider_examples_test.rb#anthropic_provider_example{ruby:line-numbers}
+
+<<< @/../test/generation_provider_examples_test.rb#google_provider_example{ruby:line-numbers}
+
+### Custom Host Configuration
+
+For Azure OpenAI or other custom endpoints:
+
+<<< @/../test/generation_provider_examples_test.rb#custom_host_configuration{ruby:line-numbers}
-# Debug with raw response data
-puts response.raw_response.inspect
-```
-:::
-The response object ensures you have full visibility into both the input prompt context and the raw provider response, making it easy to debug generation issues or access provider-specific response metadata.
diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md
index b490706f..9abec363 100644
--- a/docs/docs/getting-started.md
+++ b/docs/docs/getting-started.md
@@ -7,12 +7,14 @@ This guide will help you set up and create your first ActiveAgent application.
## Installation
-Use builder to add activeagent to your Gemfile and install:
+Use bundler to add activeagent to your Gemfile and install:
+
```bash
bundle add activeagent
```
Add the generation provider gem you want to use:
+
::: code-group
```bash [OpenAI]
@@ -32,16 +34,11 @@ bundle add ruby-openai
bundle add ruby-openai
# OpenRouter follows the same API spec as OpenAI, so you can use the same gem.
```
-:::
-Or do it manually by adding the gems to your Gemfile:
-```bash
-gem 'activeagent'
-# Add the generation provider gem you want to use, e.g.:
-gem 'ruby-openai'
-```
+:::
Then install the gems by running:
+
```bash
bundle install
```
@@ -61,18 +58,17 @@ Active Agent is designed to work seamlessly with Rails applications. It can be e
You can start by defining an `ApplicationAgent` class that inherits from `ActiveAgent::Base`. This class will define the actions and behaviors of your application's base agent. You can then use the `generate_with` method to specify the generation provider for your agent.
-```ruby
-class ApplicationAgent < ActiveAgent::Base
- generate_with :openai, instructions: "You are a helpful assistant.",
- model: "gpt-4o-mini",
- temperature: 0.7
-end
-```
-This code snippet sets up the `ApplicationAgent` to use OpenAI as the generation provider. You can replace `:openai` with any other supported provider, such as `:anthropic`, `:google`, or `:ollama`.
+<<< @/../test/dummy/app/agents/application_agent.rb {ruby}
-Now, you can interact with your application agent using the default `prompt_context` method. This method allows you to provide a context for the agent to generate a response based on the defined actions and behaviors.:
+This sets up the `ApplicationAgent` to use OpenAI as the generation provider. You can replace `:openai` with any other supported provider, such as `:anthropic`, `:google`, or `:ollama`.
-<<< @/../test/agents/application_agent_test.rb#application_agent_prompt_context_message_generation{ruby}
+Now, you can interact with your application agent using the default `prompt_context` method. This method allows you to provide a context for the agent to generate a response based on the defined actions and behaviors:
+
+<<< @/../test/agents/application_agent_test.rb#application_agent_prompt_context_message_generation{ruby:line-numbers}
+
+::: details Response Example
+
+:::
This code parameterizes the `ApplicationAgent` `with` a set of `params`.
@@ -83,26 +79,7 @@ Active Agent supports multiple generation providers, including OpenAI, Anthropic
<<< @/../test/dummy/config/active_agent.yml{yaml:line-numbers}
### Configuring custom hosts
-You can also set the host and port for the generation provider if needed. For example, if you are using a local instance of Ollama or a cloud provider's hosted instance of OpenAI, you can set the host and port as follows:
-
-```yaml
-openai: &openai
- service: "OpenAI"
- api_key: <%= Rails.application.credentials.dig(:openai, :api_key) %>
- host: "https://your-azure-openai-resource.openai.azure.com"
-```
-
-
+You can also set the host and port for the generation provider if needed. For example, if you are using a local instance of Ollama or a cloud provider's hosted instance of OpenAI, you can set the host in your configuration file as shown in the example above.
## Your First Agent
You can generate your first agent using the Rails generator. This will create a new agent class in the `app/agents` directory. It will also create a corresponding view template for the agent's actions as well as an Application Agent if you don't already have one.
@@ -112,39 +89,20 @@ $ rails generate active_agent:agent TravelAgent search book confirm
```
The `ApplicationAgent` is the base class for all agents in your application, similar to how ApplicationController is the base class for all controllers.
-```ruby [app/agents/application_agent.rb]
-class ApplicationAgent < ActiveAgent::Base
- generate_with :openai, instructions: "You are a helpful assistant.",
- model: "gpt-4o-mini",
- temperature: 0.7
- # This is the base class for all agents in your application.
- # You can define common methods and configurations here.
-end
-```
-
-The `TravelAgent` class will be generated with the specified actions: `search`, `book`, and `confirm`. Each action will be defined as a public instance method in the agent class. The generator will also create a corresponding view template for each action in the `app/views/agents/travel_agent` directory.
+The generator will create:
+- An agent class with the specified actions (`search`, `book`, and `confirm`)
+- View templates for each action in `app/views/travel_agent/`
+- An `ApplicationAgent` base class if one doesn't exist
-The JSON view is used to specify the tool schema for the action it can also be used to allow the agent to return structured data that can be used by other agents or applications. The HTML view is used to render the action's content in a web-friendly format, while the text view is used for plain text responses.
+<<< @/../test/dummy/app/agents/travel_agent.rb {ruby}
+Agent action methods are used for building Prompt context objects with Message content from rendered Action Views.
-The generated agent will look like this:
+## Action Prompts
+### Instruction messages
+### Prompt messages
-::: code-group
-```ruby [app/agents/travel_agent.rb]
-class TravelAgent < ActiveAgent::Base
- def search
- # Your search logic here
- prompt
- end
-
- def book
- # Your booking logic here
- prompt
- end
-
- def confirm
- # Your confirmation logic here
- prompt
- end
-end
-```
+Each action is defined as a public instance method that can call `prompt` to build context objects that are used to generate responses. The views define:
+- **JSON views**: Tool schemas for function calling or output schemas for structured responses
+- **HTML views**: Web-friendly formatted responses
+- **Text views**: Plain text responses
diff --git a/docs/feature-proposal-json-tool-outputs.md b/docs/feature-proposal-json-tool-outputs.md
new file mode 100644
index 00000000..6a8c796f
--- /dev/null
+++ b/docs/feature-proposal-json-tool-outputs.md
@@ -0,0 +1,216 @@
+# Feature Proposal: JSON Tool Outputs for Actions
+
+## Overview
+
+Currently, ActiveAgent supports JSON output through `output_schema` for generation providers, but actions that render tool JSON schemas with tool output schemas are not yet supported. This proposal outlines how this feature could work from a developer API perspective.
+
+## Current State
+
+- Actions can render prompts with various formats (text, html, json)
+- Generation providers support `output_schema` for structured JSON responses
+- Tools/functions are defined in the agent but don't have a way to specify output schemas for their JSON responses
+
+## Proposed Feature
+
+### 1. Action Definition with Tool Output Schema
+
+```ruby
+class TravelAgent < ApplicationAgent
+ # Define a tool with output schema
+ def book
+ prompt(
+ message: params[:message],
+ content_type: :json,
+ template: "travel_agent/book",
+ tool_output_schema: {
+ type: "object",
+ properties: {
+ booking_id: { type: "string" },
+ status: { type: "string", enum: ["confirmed", "pending", "failed"] },
+ price: { type: "number" },
+ details: {
+ type: "object",
+ properties: {
+ flight: { type: "string" },
+ hotel: { type: "string" },
+ dates: {
+ type: "object",
+ properties: {
+ check_in: { type: "string", format: "date" },
+ check_out: { type: "string", format: "date" }
+ }
+ }
+ }
+ }
+ },
+ required: ["booking_id", "status", "price"]
+ }
+ )
+ end
+end
+```
+
+### 2. Template Support
+
+The JSON template would need to conform to the defined schema:
+
+```erb
+<%# app/views/travel_agent/book.json.erb %>
+{
+ "booking_id": "<%= @prompt.booking_id %>",
+ "status": "<%= @prompt.status %>",
+ "price": <%= @prompt.price %>,
+ "details": {
+ "flight": "<%= @prompt.flight_number %>",
+ "hotel": "<%= @prompt.hotel_name %>",
+ "dates": {
+ "check_in": "<%= @prompt.check_in_date %>",
+ "check_out": "<%= @prompt.check_out_date %>"
+ }
+ }
+}
+```
+
+### 3. ActionPrompt::Base Changes
+
+The `prompt` method in `ActionPrompt::Base` would need to be updated to:
+
+1. Accept `tool_output_schema` parameter
+2. Validate the rendered JSON against the schema
+3. Include the schema in the tool definition sent to the generation provider
+
+```ruby
+# lib/active_agent/action_prompt/base.rb
+def prompt(message: nil, context: {}, content_type: nil, template: nil, tool_output_schema: nil)
+ # ... existing code ...
+
+ if tool_output_schema && content_type == :json
+ # Register this action as a tool with output schema
+ register_tool_with_schema(action_name, tool_output_schema)
+
+ # Validate rendered output against schema
+ validate_json_output(rendered_content, tool_output_schema)
+ end
+
+ # ... rest of implementation ...
+end
+```
+
+### 4. Tool Registration
+
+Tools would be automatically registered with their schemas:
+
+```ruby
+class ApplicationAgent < ActiveAgent::Base
+ def self.tools
+ @tools ||= actions.map do |action|
+ if action.tool_output_schema
+ {
+ type: "function",
+ function: {
+ name: action.name,
+ description: action.description,
+ parameters: action.input_schema,
+ output: action.tool_output_schema # New field
+ }
+ }
+ else
+ # Existing tool definition without output schema
+ end
+ end
+ end
+end
+```
+
+## Benefits
+
+1. **Type Safety**: Ensures tool outputs conform to expected schemas
+2. **Better AI Integration**: Generation providers can understand what format to expect from tools
+3. **Developer Experience**: Clear contract for what each tool returns
+4. **Documentation**: Tool output schemas serve as documentation
+
+## Implementation Considerations
+
+1. **Schema Validation**: Need to add JSON Schema validation for tool outputs
+2. **Error Handling**: What happens when output doesn't match schema?
+3. **Backwards Compatibility**: Ensure existing tools without output schemas continue to work
+4. **Generation Provider Support**: Different providers may handle tool output schemas differently
+
+## Example Use Cases
+
+### 1. E-commerce Order Processing
+```ruby
+def process_order
+ prompt(
+ message: params[:order_details],
+ content_type: :json,
+ tool_output_schema: {
+ type: "object",
+ properties: {
+ order_id: { type: "string" },
+ total: { type: "number" },
+ items: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ sku: { type: "string" },
+ quantity: { type: "integer" },
+ price: { type: "number" }
+ }
+ }
+ }
+ }
+ }
+ )
+end
+```
+
+### 2. Data Analysis Results
+```ruby
+def analyze_data
+ prompt(
+ message: params[:query],
+ content_type: :json,
+ tool_output_schema: {
+ type: "object",
+ properties: {
+ summary: { type: "string" },
+ metrics: {
+ type: "object",
+ properties: {
+ mean: { type: "number" },
+ median: { type: "number" },
+ std_dev: { type: "number" }
+ }
+ },
+ chart_data: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ x: { type: "number" },
+ y: { type: "number" }
+ }
+ }
+ }
+ }
+ }
+ )
+end
+```
+
+## Next Steps
+
+1. Prototype the changes to `ActionPrompt::Base`
+2. Add JSON Schema validation library dependency
+3. Update generation provider integrations to support tool output schemas
+4. Create comprehensive test suite
+5. Update documentation and examples
+
+## Questions for Discussion
+
+1. Should we enforce schema validation or make it optional?
+2. How should we handle schema validation errors during development vs production?
+3. Should tool output schemas be defined at the class level or action level?
+4. Do we need to support schema references ($ref) for complex schemas?
\ No newline at end of file
diff --git a/docs/public/sales_chart.png b/docs/public/sales_chart.png
new file mode 100644
index 00000000..2349d669
Binary files /dev/null and b/docs/public/sales_chart.png differ
diff --git a/lib/active_agent/action_prompt/base.rb b/lib/active_agent/action_prompt/base.rb
index 907415f6..ef05b1ff 100644
--- a/lib/active_agent/action_prompt/base.rb
+++ b/lib/active_agent/action_prompt/base.rb
@@ -233,7 +233,11 @@ def perform_actions(requested_actions:)
def perform_action(action)
current_context = context.clone
- process(action.name, *action.params)
+ # Set params from the action for controller access
+ if action.params.is_a?(Hash)
+ self.params = action.params
+ end
+ process(action.name)
context.message.role = :tool
context.message.action_id = action.id
context.message.action_name = action.name
@@ -300,9 +304,9 @@ def prompt_with(*)
def prompt(headers = {}, &block)
return context if @_prompt_was_called && headers.blank? && !block
-
# Apply option hierarchy: prompt options > agent options > config options
- merged_options = merge_options(headers)
+ merged_options = merge_options(headers[:options] || {})
+
raw_instructions = headers.has_key?(:instructions) ? headers[:instructions] : context.options[:instructions]
context.instructions = prepare_instructions(raw_instructions)
@@ -399,9 +403,8 @@ def merge_options(prompt_options)
# Extract runtime options from prompt_options (exclude instructions as it has special template logic)
runtime_options = prompt_options.slice(
:model, :temperature, :max_tokens, :stream, :top_p, :frequency_penalty,
- :presence_penalty, :response_format, :seed, :stop, :tools_choice, :user
+ :presence_penalty, :response_format, :seed, :stop, :tools_choice
)
-
# Handle explicit options parameter
explicit_options = prompt_options[:options] || {}
@@ -495,6 +498,7 @@ def collect_responses_from_templates(headers)
templates_path = headers[:template_path] || self.class.agent_name
templates_name = headers[:template_name] || action_name
each_template(Array(templates_path), templates_name).map do |template|
+ next if template.format == :json && headers[:format] != :json
format = template.format || formats.first
{
body: render(template: template, formats: [ format ]),
diff --git a/lib/active_agent/action_prompt/message.rb b/lib/active_agent/action_prompt/message.rb
index 603c4172..10023657 100644
--- a/lib/active_agent/action_prompt/message.rb
+++ b/lib/active_agent/action_prompt/message.rb
@@ -59,6 +59,30 @@ def embed
@agent_class.embed(@content)
end
+ def inspect
+ truncated_content = if @content.is_a?(String) && @content.length > 256
+ @content[0, 256] + "..."
+ elsif @content.is_a?(Array)
+ @content.map do |item|
+ if item.is_a?(String) && item.length > 256
+ item[0, 256] + "..."
+ else
+ item
+ end
+ end
+ else
+ @content
+ end
+
+ "#<#{self.class}:0x#{object_id.to_s(16)}\n" +
+ " @action_id=#{@action_id.inspect},\n" +
+ " @action_name=#{@action_name.inspect},\n" +
+ " @action_requested=#{@action_requested.inspect},\n" +
+ " @charset=#{@charset.inspect},\n" +
+ " @content=#{truncated_content.inspect},\n" +
+ " @role=#{@role.inspect}>"
+ end
+
private
def validate_role
diff --git a/lib/active_agent/action_prompt/prompt.rb b/lib/active_agent/action_prompt/prompt.rb
index 0c6f699e..734d5e65 100644
--- a/lib/active_agent/action_prompt/prompt.rb
+++ b/lib/active_agent/action_prompt/prompt.rb
@@ -79,6 +79,20 @@ def to_h
}
end
+ def inspect
+ "#<#{self.class}:0x#{object_id.to_s(16)}\n" +
+ " @options=#{@options.inspect.gsub(Rails.application.credentials.dig(:openai, :api_key), '')}\n" +
+ " @actions=#{@actions.inspect}\n" +
+ " @action_choice=#{@action_choice.inspect}\n" +
+ " @instructions=#{@instructions.inspect}\n" +
+ " @message=#{@message.inspect}\n" +
+ " @output_schema=#{@output_schema}\n" +
+ " @headers=#{@headers.inspect}\n" +
+ " @context=#{@context.inspect}\n" +
+ " @messages=#{@messages.inspect}\n" +
+ ">"
+ end
+
def headers(headers = {})
@headers.merge!(headers)
end
diff --git a/lib/active_agent/generation_provider.rb b/lib/active_agent/generation_provider.rb
index db7b3ecc..2d319142 100644
--- a/lib/active_agent/generation_provider.rb
+++ b/lib/active_agent/generation_provider.rb
@@ -24,8 +24,9 @@ def configuration(name_or_provider, **options)
end
def configure_provider(config)
- require "active_agent/generation_provider/#{config["service"].underscore}_provider"
- ActiveAgent::GenerationProvider.const_get("#{config["service"].camelize}Provider").new(config)
+ service_name = config["service"]
+ require "active_agent/generation_provider/#{service_name.underscore}_provider"
+ ActiveAgent::GenerationProvider.const_get("#{service_name.camelize}Provider").new(config)
end
def generation_provider
diff --git a/lib/active_agent/generation_provider/response.rb b/lib/active_agent/generation_provider/response.rb
index 5c047c71..dd7f53c6 100644
--- a/lib/active_agent/generation_provider/response.rb
+++ b/lib/active_agent/generation_provider/response.rb
@@ -10,6 +10,34 @@ def initialize(prompt:, message: nil, raw_response: nil)
@message = message || prompt.message
@raw_response = raw_response
end
+
+ # Extract usage statistics from the raw response
+ def usage
+ return nil unless @raw_response
+
+ # OpenAI/OpenRouter format
+ if @raw_response.is_a?(Hash) && @raw_response["usage"]
+ @raw_response["usage"]
+ # Anthropic format
+ elsif @raw_response.is_a?(Hash) && @raw_response["usage"]
+ @raw_response["usage"]
+ else
+ nil
+ end
+ end
+
+ # Helper methods for common usage stats
+ def prompt_tokens
+ usage&.dig("prompt_tokens")
+ end
+
+ def completion_tokens
+ usage&.dig("completion_tokens")
+ end
+
+ def total_tokens
+ usage&.dig("total_tokens")
+ end
end
end
end
diff --git a/lib/active_agent/parameterized.rb b/lib/active_agent/parameterized.rb
index ef1def6e..914b3686 100644
--- a/lib/active_agent/parameterized.rb
+++ b/lib/active_agent/parameterized.rb
@@ -15,12 +15,12 @@ def params
module ClassMethods
def with(params = {})
# Separate runtime options from regular params
- runtime_options = params.extract!(:model, :temperature, :max_tokens, :stream, :top_p,
- :frequency_penalty, :presence_penalty, :response_format,
- :seed, :stop, :tools_choice, :user)
+ # runtime_options = params.extract!(:model, :temperature, :max_tokens, :stream, :top_p,
+ # :frequency_penalty, :presence_penalty, :response_format,
+ # :seed, :stop, :tools_choice, :user)
- # Pass runtime options as :options parameter
- params[:options] = runtime_options if runtime_options.any?
+ # # Pass runtime options as :options parameter
+ # params[:options] = runtime_options if runtime_options.any?
ActiveAgent::Parameterized::Agent.new(self, params)
end
diff --git a/lib/active_agent/version.rb b/lib/active_agent/version.rb
index e6b41692..24518030 100644
--- a/lib/active_agent/version.rb
+++ b/lib/active_agent/version.rb
@@ -1,3 +1,3 @@
module ActiveAgent
- VERSION = "0.5.0rc2"
+ VERSION = "0.5.0rc3"
end
diff --git a/lib/generators/USAGE b/lib/generators/USAGE
index 9c7284e5..5845c318 100644
--- a/lib/generators/USAGE
+++ b/lib/generators/USAGE
@@ -3,23 +3,45 @@ Description:
configuration files and application agent class.
This generator creates:
- - Configuration file (config/active_agent.yml)
+ - Configuration file (config/active_agent.yml) unless --skip-config is used
- Application agent base class (app/agents/application_agent.rb)
- Layout templates for agent views (via template engine hooks)
+Options:
+ --skip-config Skip the creation of config/active_agent.yml
+ --formats=FORMAT Specify formats to generate layouts for (default: text)
+ Can be one or more of: text, html, json
+ Example: --formats=text html json
+
Examples:
`bin/rails generate active_agent:install`
- creates the basic ActiveAgent setup:
+ creates the basic ActiveAgent setup with default text format:
Config: config/active_agent.yml
Base Agent: app/agents/application_agent.rb
- Layouts: app/views/layouts/agent.html.erb
- app/views/layouts/agent.text.erb
+ Layout: app/views/layouts/agent.text.erb
+
+ `bin/rails generate active_agent:install --formats=text html json`
+
+ creates ActiveAgent setup with all format layouts:
+ Config: config/active_agent.yml
+ Base Agent: app/agents/application_agent.rb
+ Layouts: app/views/layouts/agent.text.erb
+ app/views/layouts/agent.html.erb
app/views/layouts/agent.json.erb
- `bin/rails generate active_agent:install --template-engine=haml`
+ `bin/rails generate active_agent:install --skip-config`
- creates ActiveAgent setup with Haml layouts instead of ERB.
+ creates ActiveAgent setup without configuration file:
+ Base Agent: app/agents/application_agent.rb
+ Layout: app/views/layouts/agent.text.erb
+
+ `bin/rails generate active_agent:install --skip-config --formats=html json`
+
+ creates ActiveAgent setup without config file but with HTML and JSON layouts:
+ Base Agent: app/agents/application_agent.rb
+ Layouts: app/views/layouts/agent.html.erb
+ app/views/layouts/agent.json.erb
After running this generator, you can create individual agents with:
`bin/rails generate active_agent:agent AGENT_NAME ACTION_NAME`
diff --git a/lib/generators/active_agent/USAGE b/lib/generators/active_agent/USAGE
index 698f3222..b5e617c1 100644
--- a/lib/generators/active_agent/USAGE
+++ b/lib/generators/active_agent/USAGE
@@ -6,25 +6,51 @@ Description:
engine and test framework generators. If no ApplicationAgent exists,
it will be created automatically.
- Views are created for each action in three formats:
- - HTML (.html.erb) - for rich text responses
- - Text (.text.erb) - for plain text responses
- - JSON (.json.erb) - for structured function calling responses
+ By default, only text format views are created. You can specify which
+ formats to generate using the --formats option:
+ - text (.text.erb) - for plain text responses (default)
+ - html (.html.erb) - for rich text responses
+ - json (.json.erb) - for structured function calling responses
+
+Options:
+ --formats=FORMAT Specify formats to generate views for (default: text)
+ Can be one or more of: text, html, json
+ Example: --formats=text html json
Examples:
`bin/rails generate active_agent:agent inventory search`
- creates an inventory agent class, views, and test:
+ creates an inventory agent class with text format view and test:
+ Agent: app/agents/inventory_agent.rb
+ View: app/views/inventory_agent/search.text.erb
+ Test: test/agents/inventory_agent_test.rb
+
+ `bin/rails generate active_agent:agent inventory search --formats=html json`
+
+ creates an inventory agent with html and json views:
Agent: app/agents/inventory_agent.rb
Views: app/views/inventory_agent/search.html.erb
- app/views/inventory_agent/search.text.erb
app/views/inventory_agent/search.json.erb
Test: test/agents/inventory_agent_test.rb
- `bin/rails generate active_agent:agent inventory search update report`
+ `bin/rails generate active_agent:agent inventory search update report --formats=text html json`
- creates an inventory agent with search, update, and report actions.
+ creates an inventory agent with search, update, and report actions in all formats:
+ Agent: app/agents/inventory_agent.rb
+ Views: app/views/inventory_agent/search.text.erb
+ app/views/inventory_agent/search.html.erb
+ app/views/inventory_agent/search.json.erb
+ app/views/inventory_agent/update.text.erb
+ app/views/inventory_agent/update.html.erb
+ app/views/inventory_agent/update.json.erb
+ app/views/inventory_agent/report.text.erb
+ app/views/inventory_agent/report.html.erb
+ app/views/inventory_agent/report.json.erb
+ Test: test/agents/inventory_agent_test.rb
- `bin/rails generate active_agent:agent admin/user create --template-engine=haml`
+ `bin/rails generate active_agent:agent admin/user create --formats=json`
- creates a namespaced admin/user agent with Haml templates.
+ creates a namespaced admin/user agent with only JSON view for API responses:
+ Agent: app/agents/admin/user_agent.rb
+ View: app/views/admin/user_agent/create.json.erb
+ Test: test/agents/admin/user_agent_test.rb
\ No newline at end of file
diff --git a/package.json b/package.json
index 8617e79b..1cf2e586 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"vitepress-plugin-group-icons": "^1.5.2"
},
"devDependencies": {
- "vitepress": "^1.6.3"
+ "vitepress": "^1.6.3",
+ "vitepress-plugin-tabs": "^0.7.1"
}
}
diff --git a/test/agents/actions_examples_test.rb b/test/agents/actions_examples_test.rb
new file mode 100644
index 00000000..a895eb20
--- /dev/null
+++ b/test/agents/actions_examples_test.rb
@@ -0,0 +1,66 @@
+require "test_helper"
+
+class ActionsExamplesTest < ActiveSupport::TestCase
+ test "using actions to prompt the agent with a templated message" do
+ # region actions_prompt_agent_basic
+ parameterized_agent = TravelAgent.with(message: "I want to find hotels in Paris")
+ travel_prompt = parameterized_agent.search
+
+ # The search action renders a view with the search results
+ assert travel_prompt.message.content.include?("Travel Search Results")
+ # endregion actions_prompt_agent_basic
+ end
+
+ test "agent uses actions with parameters" do
+ # region actions_with_parameters
+ # Pass parameters using the with method
+ agent = TravelAgent.with(
+ message: "Book this flight",
+ flight_id: "AA456",
+ passenger_name: "Alice Johnson"
+ )
+
+ # Access parameters in the action using params
+ booking_prompt = agent.book
+ assert booking_prompt.message.content.include?("AA456")
+ assert booking_prompt.message.content.include?("Alice Johnson")
+ # endregion actions_with_parameters
+ end
+
+ test "actions with different content types" do
+ # region actions_content_types
+ # HTML content for rich UI
+ search_result = TravelAgent.with(
+ departure: "NYC",
+ destination: "London",
+ results: [ { airline: "British Airways", price: 599, departure: "9:00 AM" } ]
+ ).search
+ assert search_result.message.content.include?("Travel Search Results")
+ assert search_result.message.content.include?("British Airways")
+
+ # Text content for simple responses
+ confirm_result = TravelAgent.with(
+ confirmation_number: "ABC123",
+ passenger_name: "Test User"
+ ).confirm
+ assert confirm_result.message.content.include?("Your booking has been confirmed!")
+ assert confirm_result.message.content.include?("ABC123")
+ # endregion actions_content_types
+ end
+
+ test "using prompt_context for agent-driven generation" do
+ # region actions_prompt_context_generation
+ # Use prompt_context when you want the agent to determine actions
+ agent = TravelAgent.with(message: "I need to book a flight to Paris")
+ prompt_context = agent.prompt_context
+
+ # The agent will have access to all available actions
+ assert prompt_context.actions.is_a?(Array)
+ assert prompt_context.actions.size > 0
+ # Actions are available as function schemas
+
+ # Generate a response (in real usage)
+ # response = prompt_context.generate_now
+ # endregion actions_prompt_context_generation
+ end
+end
diff --git a/test/agents/application_agent_test.rb b/test/agents/application_agent_test.rb
index a7f8e0b8..97da3b5b 100644
--- a/test/agents/application_agent_test.rb
+++ b/test/agents/application_agent_test.rb
@@ -16,8 +16,11 @@ class ApplicationAgentTest < ActiveSupport::TestCase
test_response_message_content = "It seems like you're looking for information or assistance regarding a \"Test Application Agent.\" Could you please provide more context or specify what exactly you need help with? Are you referring to a software testing agent, a specific tool, or something else? Your clarification will help me assist you better!"
# region application_agent_prompt_context_message_generation
message = "Test Application Agent"
- response = ApplicationAgent.with(message: message).prompt_context.generate_now
+ prompt = ApplicationAgent.with(message: message).prompt_context
+ response = prompt.generate_now
# endregion application_agent_prompt_context_message_generation
+
+ doc_example_output(response)
assert_equal test_response_message_content, response.message.content
end
end
@@ -33,6 +36,8 @@ class ApplicationAgentTest < ActiveSupport::TestCase
)
response = ApplicationAgent.with(message: message, messages: previous_context.messages).prompt_context.generate_now
# endregion application_agent_loaded_context_message_generation
+
+ doc_example_output(response)
assert_equal test_response_message_content, response.message.content
end
end
diff --git a/test/agents/callback_agent_test.rb b/test/agents/callback_agent_test.rb
new file mode 100644
index 00000000..2ba28a42
--- /dev/null
+++ b/test/agents/callback_agent_test.rb
@@ -0,0 +1,54 @@
+require "test_helper"
+
+class CallbackAgentTest < ActiveSupport::TestCase
+ # Create a test agent with callbacks for documentation
+ class TestCallbackAgent < ApplicationAgent
+ attr_accessor :context_set, :response_processed
+
+ # region callback_agent_before_action
+ before_action :set_context
+
+ private
+ def set_context
+ # Logic to set the context for the action
+ @context_set = true
+ prompt_context.instructions = "Context has been set"
+ end
+ # endregion callback_agent_before_action
+ end
+
+ class TestGenerationCallbackAgent < ApplicationAgent
+ attr_accessor :response_data
+
+ # region callback_agent_after_generation
+ after_generation :process_response
+
+ private
+ def process_response
+ # Access the generation provider response
+ @response_data = generation_provider.response
+ end
+ # endregion callback_agent_after_generation
+ end
+
+ test "before_action callback is executed before prompt generation" do
+ agent = TestCallbackAgent.new
+ agent.params = { message: "Test" }
+
+ # Process the agent to trigger callbacks
+ agent.process(:prompt_context)
+
+ assert agent.context_set, "before_action callback should set context"
+ end
+
+ test "after_generation callback is executed after response generation" do
+ VCR.use_cassette("callback_agent_after_generation") do
+ response = TestGenerationCallbackAgent.with(message: "Test callback").prompt_context.generate_now
+
+ # The after_generation callback should have access to the response
+ # This demonstrates the callback pattern even though we can't directly test it
+ assert_not_nil response
+ assert_not_nil response.message.content
+ end
+ end
+end
diff --git a/test/agents/data_extraction_agent_test.rb b/test/agents/data_extraction_agent_test.rb
index 36dc6a75..228d7b97 100644
--- a/test/agents/data_extraction_agent_test.rb
+++ b/test/agents/data_extraction_agent_test.rb
@@ -4,7 +4,9 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
test "describe_cat_image creates a multimodal prompt with image and text content" do
prompt = nil
VCR.use_cassette("data_extraction_agent_describe_cat_image") do
+ # region data_extraction_agent_describe_cat_image
prompt = DataExtractionAgent.describe_cat_image
+ # endregion data_extraction_agent_describe_cat_image
assert_equal "multipart/mixed", prompt.content_type
assert prompt.multimodal?
@@ -13,7 +15,10 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
end
VCR.use_cassette("data_extraction_agent_describe_cat_image_generation_response") do
+ # region data_extraction_agent_describe_cat_image_response
response = prompt.generate_now
+ # endregion data_extraction_agent_describe_cat_image_response
+ doc_example_output(response)
assert_equal response.message.content, "The cat in the image has a sleek, short coat that appears to be a grayish-brown color. Its eyes are large and striking, with a vivid green hue. The cat is sitting comfortably, being gently petted by a hand that is adorned with a bracelet. Overall, it has a calm and curious expression. The background features a dark, soft surface, adding to the cozy atmosphere of the scene."
end
@@ -22,7 +27,12 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
test "parse_resume creates a multimodal prompt with file data" do
prompt = nil
VCR.use_cassette("data_extraction_agent_parse_resume") do
- prompt = DataExtractionAgent.with(file_path: Rails.root.join("..", "..", "test", "fixtures", "files", "sample_resume.pdf")).parse_content
+ # region data_extraction_agent_parse_resume
+ prompt = DataExtractionAgent.with(
+ output_schema: :resume_schema,
+ file_path: Rails.root.join("..", "..", "test", "fixtures", "files", "sample_resume.pdf")
+ ).parse_content
+ # endregion data_extraction_agent_parse_resume
assert_equal "multipart/mixed", prompt.content_type
assert prompt.multimodal?
@@ -32,6 +42,7 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
VCR.use_cassette("data_extraction_agent_parse_resume_generation_response") do
response = prompt.generate_now
+ doc_example_output(response)
assert response.message.content.include?("John Doe")
assert response.message.content.include?("Software Engineer")
@@ -41,7 +52,12 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
test "parse_resume creates a multimodal prompt with file data with structured output schema" do
prompt = nil
VCR.use_cassette("data_extraction_agent_parse_resume_with_structured_output") do
- prompt = DataExtractionAgent.with(output_schema: :resume_schema, file_path: Rails.root.join("..", "..", "test", "fixtures", "files", "sample_resume.pdf")).parse_content
+ # region data_extraction_agent_parse_resume_with_structured_output
+ prompt = DataExtractionAgent.with(
+ output_schema: :resume_schema,
+ file_path: Rails.root.join("..", "..", "test", "fixtures", "files", "sample_resume.pdf")
+ ).parse_content
+ # endregion data_extraction_agent_parse_resume_with_structured_output
assert_equal "multipart/mixed", prompt.content_type
assert prompt.multimodal?, "Prompt should be multimodal with file data"
@@ -50,10 +66,16 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
end
VCR.use_cassette("data_extraction_agent_parse_resume_generation_response_with_structured_output") do
+ # region data_extraction_agent_parse_resume_with_structured_output_response
response = prompt.generate_now
+ # endregion data_extraction_agent_parse_resume_with_structured_output_response
+ # region data_extraction_agent_parse_resume_with_structured_output_json
json_response = JSON.parse(response.message.content)
- assert_equal "application/json", response.message.content_type
+ # endregion data_extraction_agent_parse_resume_with_structured_output_json
+ doc_example_output(response)
+ doc_example_output(json_response, "parse-resume-json-response")
+ assert_equal "application/json", response.message.content_type
assert_equal "resume_schema", response.prompt.output_schema["format"]["name"]
assert_equal json_response["name"], "John Doe"
assert_equal json_response["email"], "john.doe@example.com"
@@ -66,7 +88,12 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
test "parse_chart content from image data" do
prompt = nil
VCR.use_cassette("data_extraction_agent_parse_chart") do
- prompt = DataExtractionAgent.with(image_path: Rails.root.join("..", "..", "test", "fixtures", "images", "sales_chart.png")).parse_content
+ sales_chart_path = Rails.root.join("..", "..", "test", "fixtures", "images", "sales_chart.png")
+ # region data_extraction_agent_parse_chart
+ prompt = DataExtractionAgent.with(
+ image_path: sales_chart_path
+ ).parse_content
+ # endregion data_extraction_agent_parse_chart
assert_equal "multipart/mixed", prompt.content_type
assert prompt.multimodal?, "Prompt should be multimodal with image data"
@@ -76,6 +103,7 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
VCR.use_cassette("data_extraction_agent_parse_chart_generation_response") do
response = prompt.generate_now
+ doc_example_output(response)
assert_equal response.message.content, "The graph titled \"Quarterly Sales Report\" displays sales revenue for four quarters in 2024. Key points include:\n\n- **Q1**: Blue bar represents the lowest sales revenue.\n- **Q2**: Green bar shows an increase in sales compared to Q1.\n- **Q3**: Yellow bar continues the upward trend with higher sales than Q2.\n- **Q4**: Red bar indicates the highest sales revenue of the year.\n\nOverall, there is a clear upward trend in sales revenue over the quarters, reaching a peak in Q4."
end
@@ -84,7 +112,13 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
test "parse_chart content from image data with structured output schema" do
prompt = nil
VCR.use_cassette("data_extraction_agent_parse_chart_with_structured_output") do
- prompt = DataExtractionAgent.with(output_schema: :chart_schema, image_path: Rails.root.join("..", "..", "test", "fixtures", "images", "sales_chart.png")).parse_content
+ sales_chart_path = Rails.root.join("..", "..", "test", "fixtures", "images", "sales_chart.png")
+ # region data_extraction_agent_parse_chart_with_structured_output
+ prompt = DataExtractionAgent.with(
+ output_schema: :chart_schema,
+ image_path: sales_chart_path
+ ).parse_content
+ # endregion data_extraction_agent_parse_chart_with_structured_output
assert_equal "multipart/mixed", prompt.content_type
assert prompt.multimodal?, "Prompt should be multimodal with image data"
@@ -93,8 +127,16 @@ class DataExtractionAgentTest < ActiveSupport::TestCase
end
VCR.use_cassette("data_extraction_agent_parse_chart_generation_response_with_structured_output") do
+ # region data_extraction_agent_parse_chart_with_structured_output_response
response = prompt.generate_now
+ # endregion data_extraction_agent_parse_chart_with_structured_output_response
+
+ # region data_extraction_agent_parse_chart_with_structured_output_json
json_response = JSON.parse(response.message.content)
+ # endregion data_extraction_agent_parse_chart_with_structured_output_json
+
+ doc_example_output(response)
+ doc_example_output(json_response, "parse-chart-json-response")
assert_equal "application/json", response.message.content_type
assert_equal "chart_schema", response.prompt.output_schema["format"]["name"]
diff --git a/test/agents/messages_examples_test.rb b/test/agents/messages_examples_test.rb
new file mode 100644
index 00000000..d2eb0b5c
--- /dev/null
+++ b/test/agents/messages_examples_test.rb
@@ -0,0 +1,100 @@
+require "test_helper"
+require "active_agent/action_prompt/message"
+require "active_agent/action_prompt/action"
+
+class MessagesExamplesTest < ActiveSupport::TestCase
+ test "message structure and roles" do
+ # region messages_structure
+ # Create messages with different roles
+ system_message = ActiveAgent::ActionPrompt::Message.new(
+ role: :system,
+ content: "You are a helpful travel agent."
+ )
+
+ user_message = ActiveAgent::ActionPrompt::Message.new(
+ role: :user,
+ content: "I need to book a flight to Tokyo"
+ )
+
+ assistant_message = ActiveAgent::ActionPrompt::Message.new(
+ role: :assistant,
+ content: "I'll help you find flights to Tokyo. Let me search for available options."
+ )
+
+ # Messages have roles and content
+ assert_equal :system, system_message.role
+ assert_equal :user, user_message.role
+ assert_equal :assistant, assistant_message.role
+ # endregion messages_structure
+ end
+
+ test "messages with requested actions" do
+ # region messages_with_actions
+ # Assistant messages can include requested actions
+ message = ActiveAgent::ActionPrompt::Message.new(
+ role: :assistant,
+ content: "I'll search for flights to Paris for you.",
+ requested_actions: [
+ ActiveAgent::ActionPrompt::Action.new(
+ name: "search",
+ params: { destination: "Paris", departure_date: "2024-06-15" }
+ )
+ ]
+ )
+
+ assert message.action_requested
+ assert_equal 1, message.requested_actions.size
+ assert_equal "search", message.requested_actions.first.name
+ # endregion messages_with_actions
+ end
+
+ test "tool messages for action responses" do
+ # region tool_messages
+ # Tool messages contain results from executed actions
+ tool_message = ActiveAgent::ActionPrompt::Message.new(
+ role: :tool,
+ content: "Found 5 flights to London:\n- BA 247: $599\n- AA 106: $650\n- VS 003: $720",
+ action_name: "search",
+ action_id: "call_123abc"
+ )
+
+ assert_equal :tool, tool_message.role
+ assert_equal "search", tool_message.action_name
+ assert tool_message.content.include?("Found 5 flights")
+ # endregion tool_messages
+ end
+
+ test "building message context for prompts" do
+ # region message_context
+ # Messages form the conversation context
+ messages = [
+ ActiveAgent::ActionPrompt::Message.new(
+ role: :system,
+ content: "You are a travel booking assistant."
+ ),
+ ActiveAgent::ActionPrompt::Message.new(
+ role: :user,
+ content: "Book me a flight to Rome"
+ ),
+ ActiveAgent::ActionPrompt::Message.new(
+ role: :assistant,
+ content: "I'll help you book a flight to Rome. When would you like to travel?"
+ ),
+ ActiveAgent::ActionPrompt::Message.new(
+ role: :user,
+ content: "Next Friday"
+ )
+ ]
+
+ # Pass messages as context to agents
+ agent = TravelAgent.with(
+ message: "Find flights for next Friday",
+ messages: messages
+ )
+
+ prompt = agent.prompt_context
+ # The prompt will have the existing messages plus any added by the agent
+ assert prompt.messages.size >= 5 # At least the messages we provided
+ # endregion message_context
+ end
+end
diff --git a/test/agents/open_ai_agent_test.rb b/test/agents/open_ai_agent_test.rb
index 41886bca..4474da01 100644
--- a/test/agents/open_ai_agent_test.rb
+++ b/test/agents/open_ai_agent_test.rb
@@ -1,6 +1,6 @@
require "test_helper"
-class OpenAIAgentTest < ActiveSupport::TestCase
+class OpenAIAgentTest < ActiveAgentTestCase
test "it renders a prompt_context generates a response" do
VCR.use_cassette("openai_prompt_context_response") do
message = "Show me a cat"
@@ -15,24 +15,28 @@ class OpenAIAgentTest < ActiveSupport::TestCase
end
end
-OpenAI.configure do |config|
- config.access_token = "test-api-key"
- config.organization_id = "test-organization-id"
- config.log_errors = Rails.env.development?
- config.request_timeout = 600
-end
-
-class OpenAIClientTest < ActiveSupport::TestCase
- real_config = ActiveAgent.config
- ActiveAgent.load_configuration("")
- class OpenAIClientAgent < ApplicationAgent
- layout "agent"
- generate_with :openai
+class OpenAIClientTest < ActiveAgentTestCase
+ def setup
+ super
+ # Configure OpenAI before tests
+ OpenAI.configure do |config|
+ config.access_token = "test-api-key"
+ config.organization_id = "test-organization-id"
+ config.log_errors = Rails.env.development?
+ config.request_timeout = 600
+ end
end
test "loads configuration from environment" do
- client = OpenAI::Client.new
- assert_equal OpenAIClientAgent.generation_provider.access_token, client.access_token
- ActiveAgent.instance_variable_set(:@config, real_config)
+ # Use empty config to test environment-based configuration
+ with_active_agent_config({}) do
+ class OpenAIClientAgent < ApplicationAgent
+ layout "agent"
+ generate_with :openai
+ end
+
+ client = OpenAI::Client.new
+ assert_equal OpenAIClientAgent.generation_provider.access_token, client.access_token
+ end
end
end
diff --git a/test/agents/queued_generation_test.rb b/test/agents/queued_generation_test.rb
new file mode 100644
index 00000000..9b9c682f
--- /dev/null
+++ b/test/agents/queued_generation_test.rb
@@ -0,0 +1,29 @@
+require "test_helper"
+
+class QueuedGenerationTest < ActiveSupport::TestCase
+ include ActiveJob::TestHelper
+ test "generate_later enqueues a generation job" do
+ # region queued_generation_generate_later
+ prompt = ApplicationAgent.with(message: "Process this later").prompt_context
+
+ # Enqueue the generation job
+ assert_enqueued_with(job: ActiveAgent::GenerationJob) do
+ prompt.generate_later
+ end
+ # endregion queued_generation_generate_later
+ end
+
+ test "generate_later with custom queue and priority" do
+ # region queued_generation_custom_queue
+ prompt = ApplicationAgent.with(message: "Priority task").prompt_context
+
+ # Enqueue with specific queue and priority
+ assert_enqueued_with(
+ job: ActiveAgent::GenerationJob,
+ queue: "high_priority"
+ ) do
+ prompt.generate_later(queue: "high_priority", priority: 10)
+ end
+ # endregion queued_generation_custom_queue
+ end
+end
diff --git a/test/agents/streaming_agent_test.rb b/test/agents/streaming_agent_test.rb
index 075502d1..1320daa1 100644
--- a/test/agents/streaming_agent_test.rb
+++ b/test/agents/streaming_agent_test.rb
@@ -23,7 +23,9 @@ class StreamingAgentTest < ActiveSupport::TestCase
end
VCR.use_cassette("streaming_agent_stream_response") do
+ # region streaming_agent_stream_response
StreamingAgent.with(message: "Stream this message").prompt_context.generate_now
+ # endregion streaming_agent_stream_response
end
assert_equal broadcast_calls.size, 30
diff --git a/test/agents/support_agent_test.rb b/test/agents/support_agent_test.rb
index e722b663..d6f5966c 100644
--- a/test/agents/support_agent_test.rb
+++ b/test/agents/support_agent_test.rb
@@ -8,10 +8,16 @@ class SupportAgentTest < ActiveSupport::TestCase
test "it renders a prompt_context generates a response with a tool call and performs the requested actions" do
VCR.use_cassette("support_agent_prompt_context_tool_call_response") do
+ # region support_agent_tool_call
message = "Show me a cat"
prompt = SupportAgent.with(message: message).prompt_context
+ # endregion support_agent_tool_call
assert_equal message, prompt.message.content
+ # region support_agent_tool_call_response
response = prompt.generate_now
+ # endregion support_agent_tool_call_response
+
+ doc_example_output(response)
assert_equal 4, response.prompt.messages.size
assert_equal :system, response.prompt.messages[0].role
assert_equal :user, response.prompt.messages[1].role
diff --git a/test/agents/translation_agent_test.rb b/test/agents/translation_agent_test.rb
index 20f7a989..288349ef 100644
--- a/test/agents/translation_agent_test.rb
+++ b/test/agents/translation_agent_test.rb
@@ -13,9 +13,14 @@ class TranslationAgentTest < ActiveSupport::TestCase
test "it renders a translate prompt and generates a translation" do
VCR.use_cassette("translation_agent_direct_prompt_generation") do
# region translation_agent_translate_prompt_generation
- response = TranslationAgent.with(message: "Hi, I'm Justin", locale: "japanese").translate.generate_now
+ response = TranslationAgent.with(
+ message: "Hi, I'm Justin",
+ locale: "japanese"
+ ).translate.generate_now
assert_equal "こんにちは、私はジャスティンです。", response.message.content
# endregion translation_agent_translate_prompt_generation
+
+ doc_example_output(response)
end
end
end
diff --git a/test/agents/travel_agent_test.rb b/test/agents/travel_agent_test.rb
new file mode 100644
index 00000000..9f208f9d
--- /dev/null
+++ b/test/agents/travel_agent_test.rb
@@ -0,0 +1,201 @@
+require "test_helper"
+require "ostruct"
+
+class TravelAgentTest < ActiveAgentTestCase
+ test "travel agent with instructions template" do
+ # region travel_agent_instructions_template
+ user = OpenStruct.new(name: "Alice Johnson")
+ prompt = TravelAgent.with(user: user, message: "I need help planning my trip").prompt_context
+
+ # The instructions template should be included in the prompt
+ system_message = prompt.messages.find { |m| m.role == :system }
+ assert_not_nil system_message
+ assert_includes system_message.content, "Alice Johnson"
+ assert_includes system_message.content, "`search` action to find hotels"
+ assert_includes system_message.content, "`book` action to book a hotel"
+ assert_includes system_message.content, "`confirm` action to confirm the booking"
+ # endregion travel_agent_instructions_template
+ end
+
+ test "travel agent with custom user in instructions" do
+ VCR.use_cassette("travel_agent_custom_user_instructions") do
+ # region travel_agent_custom_user_instructions
+ user = OpenStruct.new(name: "Bob Smith")
+ message = "I need to find a hotel in Paris"
+ prompt = TravelAgent.with(user: user, message: message).prompt_context
+ response = prompt.generate_now
+
+ # The instructions should have been personalized with the user's name
+ system_message = response.prompt.messages.find { |m| m.role == :system }
+ assert_includes system_message.content, "Bob Smith"
+
+ # The agent should understand the task based on the instructions
+ assert_not_nil response
+ assert_not_nil response.message
+ # endregion travel_agent_custom_user_instructions
+
+ doc_example_output(response)
+ end
+ end
+
+ test "travel agent search action with LLM interaction" do
+ VCR.use_cassette("travel_agent_search_llm") do
+ # region travel_agent_search_llm
+ message = "Find flights from NYC to LAX"
+ prompt = TravelAgent.with(message: message).prompt_context
+ response = prompt.generate_now
+
+ # The LLM should call the search tool and return results
+ assert_not_nil response
+ assert_not_nil response.message
+ assert response.prompt.messages.size >= 2 # At least system and user messages
+ # endregion travel_agent_search_llm
+
+ doc_example_output(response)
+ end
+ end
+
+ test "travel agent book action with LLM interaction" do
+ VCR.use_cassette("travel_agent_book_llm") do
+ # region travel_agent_book_llm
+ message = "Book flight AA123 for John Doe"
+ prompt = TravelAgent.with(message: message).prompt_context
+ response = prompt.generate_now
+
+ # The LLM should call the book tool
+ assert_not_nil response
+ assert_not_nil response.message
+ assert response.prompt.messages.size >= 2
+ # endregion travel_agent_book_llm
+
+ doc_example_output(response, "travel_agent_book_llm")
+ end
+ end
+
+ test "travel agent confirm action with LLM interaction" do
+ VCR.use_cassette("travel_agent_confirm_llm") do
+ # region travel_agent_confirm_llm
+ message = "Confirm booking TRV789012 for Jane Smith"
+ prompt = TravelAgent.with(message: message).prompt_context
+ response = prompt.generate_now
+
+ # The LLM should call the confirm tool
+ assert_not_nil response
+ assert_not_nil response.message
+ assert response.prompt.messages.size >= 2
+ # endregion travel_agent_confirm_llm
+
+ doc_example_output(response)
+ end
+ end
+
+ test "travel agent full conversation flow with LLM" do
+ VCR.use_cassette("travel_agent_conversation_flow") do
+ # region travel_agent_conversation_flow
+ # Test a full conversation flow with the LLM
+ message = "I need to search for flights from NYC to LAX, then book one and confirm it"
+ prompt = TravelAgent.with(message: message).prompt_context
+ response = prompt.generate_now
+
+ # The LLM should understand the request and potentially call multiple tools
+ assert_not_nil response
+ assert_not_nil response.message
+ assert response.prompt.messages.size >= 2 # At least system and user messages
+ # endregion travel_agent_conversation_flow
+
+ doc_example_output(response)
+ end
+ end
+
+ # Keep the original tests to ensure the views still work correctly
+ test "travel agent search view renders HTML format" do
+ # region travel_agent_search_html
+ response = TravelAgent.with(
+ message: "Find flights from NYC to LAX",
+ departure: "NYC",
+ destination: "LAX",
+ results: [
+ { airline: "American Airlines", price: 299, departure: "10:00 AM" },
+ { airline: "Delta", price: 350, departure: "2:00 PM" }
+ ]
+ ).search
+
+ # The HTML view will be rendered with flight search results
+ assert response.message.content.include?("Travel Search Results")
+ assert response.message.content.include?("NYC")
+ assert response.message.content.include?("LAX")
+ # endregion travel_agent_search_html
+
+ doc_example_output(response)
+ end
+
+ test "travel agent book view renders text format" do
+ # region travel_agent_book_text
+ response = TravelAgent.with(
+ message: "Book flight AA123",
+ flight_id: "AA123",
+ passenger_name: "John Doe",
+ confirmation_number: "CNF123456"
+ ).book
+
+ # The text view returns booking details
+ assert response.message.content.include?("Booking flight AA123")
+ assert response.message.content.include?("Passenger: John Doe")
+ assert response.message.content.include?("Confirmation: CNF123456")
+ assert response.message.content.include?("Status: Booking confirmed")
+ # endregion travel_agent_book_text
+
+ doc_example_output(response, "travel_agent_book_text")
+ end
+
+ test "travel agent confirm view renders text format" do
+ # region travel_agent_confirm_text
+ response = TravelAgent.with(
+ message: "Confirm booking",
+ confirmation_number: "TRV789012",
+ passenger_name: "Jane Smith",
+ flight_details: "AA123 - NYC to LAX, departing 10:00 AM"
+ ).confirm
+
+ # The text view returns a simple confirmation message
+ assert response.message.content.include?("Your booking has been confirmed!")
+ assert response.message.content.include?("TRV789012")
+ assert response.message.content.include?("Jane Smith")
+ # endregion travel_agent_confirm_text
+
+ doc_example_output(response)
+ end
+
+ test "travel agent demonstrates multi-format support" do
+ # region travel_agent_multi_format
+ # Different actions use different formats based on their purpose
+ search_response = TravelAgent.with(
+ message: "Search flights",
+ departure: "NYC",
+ destination: "LAX",
+ results: []
+ ).search
+ assert search_response.message.content.include?("Travel Search Results") # Rich UI format
+
+ book_response = TravelAgent.with(
+ message: "Book flight",
+ flight_id: "AA123",
+ passenger_name: "Test User",
+ confirmation_number: "CNF789"
+ ).book
+ assert book_response.message.content.include?("Booking flight AA123") # Text format
+ assert book_response.message.content.include?("Test User")
+
+ confirm_response = TravelAgent.with(
+ message: "Confirm",
+ confirmation_number: "CNF789",
+ passenger_name: "Test User"
+ ).confirm
+ assert confirm_response.message.content.include?("Your booking has been confirmed!") # Simple text format
+ # endregion travel_agent_multi_format
+
+ assert_not_nil search_response
+ assert_not_nil book_response
+ assert_not_nil confirm_response
+ end
+end
diff --git a/test/agents/travel_agent_tool_call_test.rb b/test/agents/travel_agent_tool_call_test.rb
new file mode 100644
index 00000000..a4780c3e
--- /dev/null
+++ b/test/agents/travel_agent_tool_call_test.rb
@@ -0,0 +1,143 @@
+require "test_helper"
+
+class TravelAgentToolCallTest < ActiveAgentTestCase
+ test "assistant tool call message contains flat params" do
+ # Create a mock tool call action with flat params structure
+ action = ActiveAgent::ActionPrompt::Action.new(
+ id: "call_123",
+ name: "search",
+ params: { departure: "NYC", destination: "LAX" }
+ )
+
+ assert_equal "search", action.name
+ assert_equal({ departure: "NYC", destination: "LAX" }, action.params)
+ end
+
+ test "travel agent search action receives params through perform_action" do
+ # Create agent with context
+ agent = TravelAgent.new
+ agent.context = ActiveAgent::ActionPrompt::Prompt.new
+
+ # Mock a tool call action
+ action = ActiveAgent::ActionPrompt::Action.new(
+ id: "call_search_123",
+ name: "search",
+ params: { departure: "NYC", destination: "LAX", results: [] }
+ )
+
+ # Call perform_action
+ agent.send(:perform_action, action)
+
+ # Verify the action can access params
+ assert_equal "NYC", agent.instance_variable_get(:@departure)
+ assert_equal "LAX", agent.instance_variable_get(:@destination)
+
+ # Verify context was updated with tool message
+ assert_equal :tool, agent.context.message.role
+ assert_equal "call_search_123", agent.context.message.action_id
+ assert_equal "search", agent.context.message.action_name
+ assert agent.context.messages.last.role == :tool
+ end
+
+ test "travel agent book action receives params through perform_action" do
+ # Create agent with context
+ agent = TravelAgent.new
+ agent.context = ActiveAgent::ActionPrompt::Prompt.new
+
+ # Mock a tool call action
+ action = ActiveAgent::ActionPrompt::Action.new(
+ id: "call_book_456",
+ name: "book",
+ params: { flight_id: "AA123", passenger_name: "John Doe" }
+ )
+
+ # Call perform_action
+ agent.send(:perform_action, action)
+
+ # Verify the action can access params
+ assert_equal "AA123", agent.instance_variable_get(:@flight_id)
+ assert_equal "John Doe", agent.instance_variable_get(:@passenger_name)
+
+ # Verify context was updated with tool message
+ assert_equal :tool, agent.context.message.role
+ assert_equal "call_book_456", agent.context.message.action_id
+ assert_equal "book", agent.context.message.action_name
+ assert agent.context.messages.last.role == :tool
+ end
+
+ test "travel agent confirm action receives params through perform_action" do
+ # Create agent with context
+ agent = TravelAgent.new
+ agent.context = ActiveAgent::ActionPrompt::Prompt.new
+
+ # Mock a tool call action
+ action = ActiveAgent::ActionPrompt::Action.new(
+ id: "call_confirm_789",
+ name: "confirm",
+ params: { confirmation_number: "CNF789", passenger_name: "Jane Smith" }
+ )
+
+ # Call perform_action
+ agent.send(:perform_action, action)
+
+ # Verify the action can access params
+ assert_equal "CNF789", agent.instance_variable_get(:@confirmation_number)
+ assert_equal "Jane Smith", agent.instance_variable_get(:@passenger_name)
+
+ # Verify context was updated with tool message
+ assert_equal :tool, agent.context.message.role
+ assert_equal "call_confirm_789", agent.context.message.action_id
+ assert_equal "confirm", agent.context.message.action_name
+ assert agent.context.messages.last.role == :tool
+ end
+
+ test "perform_action sets params and updates context messages" do
+ # Create agent
+ agent = TravelAgent.new
+
+ # Mock a tool call action with flat params
+ action = ActiveAgent::ActionPrompt::Action.new(
+ id: "call_456",
+ name: "search",
+ params: { departure: "NYC", destination: "LAX" }
+ )
+
+ # Create a context with initial messages
+ agent.context = ActiveAgent::ActionPrompt::Prompt.new
+ agent.context.messages = [
+ ActiveAgent::ActionPrompt::Message.new(role: :system, content: "You are a travel agent"),
+ ActiveAgent::ActionPrompt::Message.new(role: :user, content: "Find flights")
+ ]
+ initial_message_count = agent.context.messages.size
+
+ # Call perform_action
+ agent.send(:perform_action, action)
+
+ # Verify params were set correctly from flat structure
+ assert_equal({ departure: "NYC", destination: "LAX" }, agent.params)
+
+ # Verify context was updated with tool message
+ assert_equal initial_message_count + 1, agent.context.messages.size
+ last_message = agent.context.messages.last
+ assert_equal :tool, last_message.role
+ assert_equal "call_456", last_message.action_id
+ assert_equal "search", last_message.action_name
+ assert_equal "call_456", last_message.generation_id
+ end
+
+ test "tool schema uses flat parameter structure" do
+ agent = TravelAgent.new
+ agent.context = ActiveAgent::ActionPrompt::Prompt.new
+
+ # Load the search action schema
+ schema = agent.send(:load_schema, "search", [ "travel_agent" ])
+
+ # Verify the schema has flat structure
+ assert_equal "function", schema["type"]
+ assert_equal "search", schema["function"]["name"]
+ assert_equal "object", schema["function"]["parameters"]["type"]
+ assert schema["function"]["parameters"]["properties"].key?("departure")
+ assert schema["function"]["parameters"]["properties"].key?("destination")
+ assert_includes schema["function"]["parameters"]["required"], "destination"
+ end
+end
diff --git a/test/callbacks_test.rb b/test/callbacks_test.rb
index 0669534f..da2b2b84 100644
--- a/test/callbacks_test.rb
+++ b/test/callbacks_test.rb
@@ -33,7 +33,7 @@ def another_test_action
end
def stream_action
- prompt_context(message: "Stream this message", stream: true)
+ prompt_context(message: "Stream this message", options: { stream: true })
end
private
diff --git a/test/configuration_examples_test.rb b/test/configuration_examples_test.rb
new file mode 100644
index 00000000..7b56b8f8
--- /dev/null
+++ b/test/configuration_examples_test.rb
@@ -0,0 +1,92 @@
+require "test_helper"
+
+class ConfigurationExamplesTest < ActiveAgentTestCase
+ # Test agents for documentation examples
+
+ # region application_agent_basic_configuration
+ class ExampleApplicationAgent < ActiveAgent::Base
+ generate_with :openai,
+ instructions: "You are a helpful assistant.",
+ model: "gpt-4o-mini",
+ temperature: 0.7
+ end
+ # endregion application_agent_basic_configuration
+
+ # region travel_agent_example
+ class TravelAgent < ApplicationAgent
+ def search
+ # Your search logic here
+ prompt
+ end
+
+ def book
+ # Your booking logic here
+ prompt
+ end
+
+ def confirm
+ # Your confirmation logic here
+ prompt
+ end
+ end
+ # endregion travel_agent_example
+
+ # region travel_agent_with_views
+ class TravelAgentWithViews < ApplicationAgent
+ def search
+ @results = fetch_travel_options
+ @departure = params[:departure]
+ @destination = params[:destination]
+ prompt
+ end
+
+ private
+ def fetch_travel_options
+ # Mock travel options for documentation
+ [
+ { airline: "United", price: 450, departure: "09:00" },
+ { airline: "Delta", price: 425, departure: "14:30" }
+ ]
+ end
+ end
+ # endregion travel_agent_with_views
+
+ # region parameterized_agent_example
+ class ParameterizedAgent < ApplicationAgent
+ def analyze
+ # Access parameters passed to the agent
+ @topic = params[:topic]
+ @depth = params[:depth] || "medium"
+ prompt
+ end
+ end
+ # endregion parameterized_agent_example
+
+ test "application agent is configured correctly" do
+ # _generation_provider returns the provider instance, not symbol
+ assert ExampleApplicationAgent._generation_provider.is_a?(ActiveAgent::GenerationProvider::OpenAIProvider)
+ # The configuration is stored in the provider's config
+ provider = ExampleApplicationAgent._generation_provider
+ assert_equal "gpt-4o-mini", provider.instance_variable_get(:@model_name)
+ end
+
+ test "travel agent has required actions" do
+ agent = TravelAgent.new
+ assert_respond_to agent, :search
+ assert_respond_to agent, :book
+ assert_respond_to agent, :confirm
+ end
+
+ test "parameterized agent accesses params" do
+ # region parameterized_agent_usage
+ agent = ParameterizedAgent.with(topic: "AI Safety", depth: "detailed")
+ # Agent is parameterized with topic and depth
+ # These params are accessible in the agent's actions
+ # endregion parameterized_agent_usage
+
+ # The params are stored internally and accessed via params method in actions
+ assert_not_nil agent
+ # agent.with returns a ParameterizedAgent instance wrapped in a delegator
+ assert_respond_to agent, :analyze
+ end
+end
diff --git a/test/configuration_test.rb b/test/configuration_test.rb
index a077c392..b423baf6 100644
--- a/test/configuration_test.rb
+++ b/test/configuration_test.rb
@@ -1,18 +1,7 @@
require "test_helper"
# Test for Active Agent configuration loading and validation
-class ActiveAgentConfigurationTest < ActiveSupport::TestCase
- def setup
- @original_config = ActiveAgent.config
- @original_rails_env = ENV["RAILS_ENV"]
- end
-
- def teardown
- ActiveAgent.instance_variable_set(:@config, @original_config) if @original_config
- ENV["RAILS_ENV"] = "test"
- ActiveAgent.load_configuration(Rails.root.join("config/active_agent.yml"))
- end
-
+class ActiveAgentConfigurationTest < ActiveAgentTestCase
test "loads configuration from active_agent.yml file" do
ActiveAgent.instance_variable_set(:@config, nil)
# Test loading from the actual dummy app configuration
diff --git a/test/dummy/app/agents/anthropic_agent.rb b/test/dummy/app/agents/anthropic_agent.rb
new file mode 100644
index 00000000..8bad835d
--- /dev/null
+++ b/test/dummy/app/agents/anthropic_agent.rb
@@ -0,0 +1,3 @@
+class AnthropicAgent < ActiveAgent::Base
+ generate_with :anthropic
+end
diff --git a/test/dummy/app/agents/application_agent.rb b/test/dummy/app/agents/application_agent.rb
index ad1ad336..b002bb4e 100644
--- a/test/dummy/app/agents/application_agent.rb
+++ b/test/dummy/app/agents/application_agent.rb
@@ -1,5 +1,5 @@
class ApplicationAgent < ActiveAgent::Base
layout "agent"
- generate_with :openai, model: "gpt-4o-mini", instructions: "You are a helpful assistant."
+ generate_with :openai, model: "gpt-4o-mini"
end
diff --git a/test/dummy/app/agents/data_extraction_agent.rb b/test/dummy/app/agents/data_extraction_agent.rb
index b2f23137..793543df 100644
--- a/test/dummy/app/agents/data_extraction_agent.rb
+++ b/test/dummy/app/agents/data_extraction_agent.rb
@@ -1,12 +1,20 @@
class DataExtractionAgent < ApplicationAgent
before_action :set_multimodal_content, only: [ :parse_content ]
- def describe_cat_image
- prompt(message: "Describe the cat in the image", image_data: CatImageService.fetch_base64_image)
+ def parse_content
+ prompt(
+ message: params[:message] || "Parse the content of the file or image",
+ image_data: @image_data,
+ file_data: @file_data,
+ output_schema: params[:output_schema]
+ )
end
- def parse_content
- prompt(message: "Parse the resume", image_data: @image_data, file_data: @file_data, output_schema: params[:output_schema])
+ def describe_cat_image
+ prompt(
+ message: "Describe the cat in the image",
+ image_data: CatImageService.fetch_base64_image
+ )
end
private
diff --git a/test/dummy/app/agents/travel_agent.rb b/test/dummy/app/agents/travel_agent.rb
new file mode 100644
index 00000000..f029ef4c
--- /dev/null
+++ b/test/dummy/app/agents/travel_agent.rb
@@ -0,0 +1,30 @@
+class TravelAgent < ApplicationAgent
+ before_action :set_user
+
+ def search
+ @departure = params[:departure]
+ @destination = params[:destination]
+ @results = params[:results] || []
+ prompt(content_type: :html)
+ end
+
+ def book
+ @flight_id = params[:flight_id]
+ @passenger_name = params[:passenger_name]
+ @confirmation_number = params[:confirmation_number]
+ prompt(content_type: :text)
+ end
+
+ def confirm
+ @confirmation_number = params[:confirmation_number]
+ @passenger_name = params[:passenger_name]
+ @flight_details = params[:flight_details]
+ prompt(content_type: :text)
+ end
+
+ private
+
+ def set_user
+ @user = params[:user] || OpenStruct.new(name: "Guest")
+ end
+end
diff --git a/test/dummy/app/views/application_agent/instructions.text.erb b/test/dummy/app/views/application_agent/instructions.text.erb
new file mode 100644
index 00000000..e69de29b
diff --git a/test/dummy/app/views/translation_agent/translate.json.jbuilder b/test/dummy/app/views/translation_agent/translate.json.jbuilder
new file mode 100644
index 00000000..9c4ead9f
--- /dev/null
+++ b/test/dummy/app/views/translation_agent/translate.json.jbuilder
@@ -0,0 +1,18 @@
+json.type :function
+json.function do
+ json.name action_name
+ json.description "This action takes params locale and message and returns a translated message."
+ json.parameters do
+ json.type :object
+ json.properties do
+ json.locale do
+ json.type :string
+ json.description "The target language for translation."
+ end
+ json.message do
+ json.type :string
+ json.description "The text to be translated."
+ end
+ end
+ end
+end
diff --git a/test/dummy/app/views/travel_agent/book.json.jbuilder b/test/dummy/app/views/travel_agent/book.json.jbuilder
new file mode 100644
index 00000000..c6265a2a
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/book.json.jbuilder
@@ -0,0 +1,29 @@
+json.type :function
+json.function do
+ json.name action_name
+ json.description "Book a specific flight"
+ json.parameters do
+ json.type :object
+ json.properties do
+ json.params do
+ json.type :object
+ json.properties do
+ json.flight_id do
+ json.type :string
+ json.description "The flight identifier to book"
+ end
+ json.passenger_name do
+ json.type :string
+ json.description "Name of the passenger"
+ end
+ json.passenger_email do
+ json.type :string
+ json.description "Email address for booking confirmation"
+ end
+ end
+ json.required [ "flight_id", "passenger_name" ]
+ end
+ end
+ json.required [ "params" ]
+ end
+end
diff --git a/test/dummy/app/views/travel_agent/book.text.erb b/test/dummy/app/views/travel_agent/book.text.erb
new file mode 100644
index 00000000..d8e46d88
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/book.text.erb
@@ -0,0 +1,4 @@
+Booking flight <%= @flight_id %>
+Passenger: <%= @passenger_name %>
+Confirmation: <%= @confirmation_number %>
+Status: Booking confirmed
\ No newline at end of file
diff --git a/test/dummy/app/views/travel_agent/confirm.json.jbuilder b/test/dummy/app/views/travel_agent/confirm.json.jbuilder
new file mode 100644
index 00000000..d1a646ad
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/confirm.json.jbuilder
@@ -0,0 +1,25 @@
+json.type :function
+json.function do
+ json.name action_name
+ json.description "Confirm a flight booking"
+ json.parameters do
+ json.type :object
+ json.properties do
+ json.params do
+ json.type :object
+ json.properties do
+ json.confirmation_number do
+ json.type :string
+ json.description "The booking confirmation number"
+ end
+ json.send_email do
+ json.type :boolean
+ json.description "Whether to send confirmation email"
+ end
+ end
+ json.required [ "confirmation_number" ]
+ end
+ end
+ json.required [ "params" ]
+ end
+end
diff --git a/test/dummy/app/views/travel_agent/confirm.text.erb b/test/dummy/app/views/travel_agent/confirm.text.erb
new file mode 100644
index 00000000..9cd30941
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/confirm.text.erb
@@ -0,0 +1,7 @@
+Your booking has been confirmed!
+
+Confirmation Number: <%= @confirmation_number || "TRV123456" %>
+Passenger: <%= @passenger_name || "Guest" %>
+Flight: <%= @flight_details || "Flight details will be sent via email" %>
+
+Thank you for choosing our travel service.
\ No newline at end of file
diff --git a/test/dummy/app/views/travel_agent/instructions.text.erb b/test/dummy/app/views/travel_agent/instructions.text.erb
new file mode 100644
index 00000000..953a2244
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/instructions.text.erb
@@ -0,0 +1,11 @@
+This agent is currently interacting with <%= @user.name %> to find a hotel near their travel destination.
+The agent should use the following actions to achieve the desired outcome:
+
+<% controller.action_schemas.each do |action| %>
+ <%= action["function"]["name"] %>: <%= action["function"]["description"] %>
+<% end %>
+
+requirements:
+- The agent should use the `search` action to find hotels in the requested location.
+- The agent should use the `book` action to book a hotel for the user.
+- The agent should use the `confirm` action to confirm the booking with the user.
\ No newline at end of file
diff --git a/test/dummy/app/views/travel_agent/search.html.erb b/test/dummy/app/views/travel_agent/search.html.erb
new file mode 100644
index 00000000..9cc54c96
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/search.html.erb
@@ -0,0 +1,14 @@
+Travel Search Results
+
+Searching for flights from <%= @departure %> to <%= @destination %>
+
+
+<% @results.each do |flight| %>
+ -
+ <%= flight[:airline] %> - $<%= flight[:price] %>
+
Departure: <%= flight[:departure] %>
+
+<% end %>
+
+
+Would you like to book any of these flights?
\ No newline at end of file
diff --git a/test/dummy/app/views/travel_agent/search.json.jbuilder b/test/dummy/app/views/travel_agent/search.json.jbuilder
new file mode 100644
index 00000000..4f049263
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/search.json.jbuilder
@@ -0,0 +1,23 @@
+json.type :function
+json.function do
+ json.name action_name
+ json.description "Search for available flights to a destination"
+ json.parameters do
+ json.type :object
+ json.properties do
+ json.departure do
+ json.type :string
+ json.description "Departure city or airport code"
+ end
+ json.destination do
+ json.type :string
+ json.description "Destination city or airport code"
+ end
+ json.date do
+ json.type :string
+ json.description "Travel date in YYYY-MM-DD format"
+ end
+ end
+ json.required [ "destination" ]
+ end
+end
diff --git a/test/dummy/app/views/travel_agent/search.text.erb b/test/dummy/app/views/travel_agent/search.text.erb
new file mode 100644
index 00000000..7a5a31d6
--- /dev/null
+++ b/test/dummy/app/views/travel_agent/search.text.erb
@@ -0,0 +1,11 @@
+Travel Search Results
+====================
+
+Searching for flights from <%= @departure %> to <%= @destination %>
+
+Available flights:
+<% @results.each_with_index do |flight, i| %>
+<%= i + 1 %>. <%= flight[:airline] %> - $<%= flight[:price] %> (Departure: <%= flight[:departure] %>)
+<% end %>
+
+Please let me know which flight you'd like to book.
\ No newline at end of file
diff --git a/test/fixtures/vcr_cassettes/callback_agent_after_generation.yml b/test/fixtures/vcr_cassettes/callback_agent_after_generation.yml
new file mode 100644
index 00000000..e537f67f
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/callback_agent_after_generation.yml
@@ -0,0 +1,119 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"Test
+ callback","type":"text/plain","charset":"UTF-8"}],"temperature":0.7}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Wed, 30 Jul 2025 23:28:09 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '557'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '561'
+ X-Ratelimit-Limit-Requests:
+ - '10000'
+ X-Ratelimit-Limit-Tokens:
+ - '200000'
+ X-Ratelimit-Remaining-Requests:
+ - '9999'
+ X-Ratelimit-Remaining-Tokens:
+ - '199986'
+ X-Ratelimit-Reset-Requests:
+ - 8.64s
+ X-Ratelimit-Reset-Tokens:
+ - 4ms
+ X-Request-Id:
+ - 7607f275-ad73-440a-ad90-2c981b070fa0
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=ZBPuveBeMta1Vr.Pdr1kVTo_KhFMM.N_GM7FgjyFubc-1753918089-1.0.1.1-1n_VmH3bTOH01XwrZI3uhBSkW8zB3ZE69OlEszBHMMrS0ZwS1evCrmuKlJwGFBHl.OJy72iMpTDWgh0hS_GNdWhx.lPXm.NTpZQg7VRBVeI;
+ path=/; expires=Wed, 30-Jul-25 23:58:09 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=19jGPNkuEG7yDAvjw.x_qCObeJC7.piiEwRJNdWFhXo-1753918089790-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 9678a1769923eb2a-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-BzAVNZRgJPseNjV7ePRhpRUEJ9Ay5",
+ "object": "chat.completion",
+ "created": 1753918089,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "It looks like you are testing a callback. How can I assist you further?",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 19,
+ "completion_tokens": 16,
+ "total_tokens": 35,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_62a23a81ef"
+ }
+ recorded_at: Wed, 30 Jul 2025 23:28:09 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/fixtures/vcr_cassettes/generation_response_usage_example.yml b/test/fixtures/vcr_cassettes/generation_response_usage_example.yml
new file mode 100644
index 00000000..43e9cc16
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/generation_response_usage_example.yml
@@ -0,0 +1,118 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"Hello","type":"text/plain","charset":"UTF-8"}],"temperature":0.7}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Wed, 30 Jul 2025 23:23:57 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '378'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '389'
+ X-Ratelimit-Limit-Requests:
+ - '10000'
+ X-Ratelimit-Limit-Tokens:
+ - '200000'
+ X-Ratelimit-Remaining-Requests:
+ - '9999'
+ X-Ratelimit-Remaining-Tokens:
+ - '199988'
+ X-Ratelimit-Reset-Requests:
+ - 8.64s
+ X-Ratelimit-Reset-Tokens:
+ - 3ms
+ X-Request-Id:
+ - 2da71a7b-f856-4bf7-a145-9bf2497bec77
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=EQtmnUQZbrOtYsGKLanTp6LKCt7JA4TuWK3VyVXPnIE-1753917837-1.0.1.1-3vTIRQXDg8RMIPadVh81NqbO.AZkYtOOnhvAbvfMuRdQIKnU7140CR3QJLCh3L.8UVzIy2nThFBcp0sR_jqsiip3bt563.c1W_gPUojj6CM;
+ path=/; expires=Wed, 30-Jul-25 23:53:57 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=JN0fhvE__wJrPXni3zi.l48mMNXaeKiULJSWxxwELr0-1753917837896-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 96789b5189bdcf97-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-BzARJpY4my8FBbqhLS0I7Qo2WMDmY",
+ "object": "chat.completion",
+ "created": 1753917837,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "Hello! How can I assist you today?",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 18,
+ "completion_tokens": 9,
+ "total_tokens": 27,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_34a54ae93c"
+ }
+ recorded_at: Wed, 30 Jul 2025 23:23:57 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/fixtures/vcr_cassettes/travel_agent_book_llm.yml b/test/fixtures/vcr_cassettes/travel_agent_book_llm.yml
new file mode 100644
index 00000000..424a9df8
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/travel_agent_book_llm.yml
@@ -0,0 +1,142 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"Book
+ flight AA123 for John Doe","type":"text/plain","charset":"UTF-8"}],"temperature":0.7,"tools":[{"type":"function","function":{"name":"search","description":"Search
+ for available flights to a destination","parameters":{"type":"object","properties":{"departure":{"type":"string","description":"Departure
+ city or airport code"},"destination":{"type":"string","description":"Destination
+ city or airport code"},"date":{"type":"string","description":"Travel date
+ in YYYY-MM-DD format"}},"required":["destination"]}}},{"type":"function","function":{"name":"book","description":"Book
+ a specific flight","parameters":{"type":"object","properties":{"flight_id":{"type":"string","description":"The
+ flight identifier to book"},"passenger_name":{"type":"string","description":"Name
+ of the passenger"},"passenger_email":{"type":"string","description":"Email
+ address for booking confirmation"}},"required":["flight_id","passenger_name"]}}},{"type":"function","function":{"name":"confirm","description":"Confirm
+ a flight booking","parameters":{"type":"object","properties":{"confirmation_number":{"type":"string","description":"The
+ booking confirmation number"},"send_email":{"type":"boolean","description":"Whether
+ to send confirmation email"}},"required":["confirmation_number"]}}}]}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Fri, 01 Aug 2025 02:05:28 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '758'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '869'
+ X-Ratelimit-Limit-Requests:
+ - '10000'
+ X-Ratelimit-Limit-Tokens:
+ - '200000'
+ X-Ratelimit-Remaining-Requests:
+ - '9999'
+ X-Ratelimit-Remaining-Tokens:
+ - '199982'
+ X-Ratelimit-Reset-Requests:
+ - 8.64s
+ X-Ratelimit-Reset-Tokens:
+ - 5ms
+ X-Request-Id:
+ - req_d751f03bac914d2bba1c85d03550ebd8
+ X-Envoy-Decorator-Operation:
+ - router.openai.svc.cluster.local:5004/*
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=GyPOodysF7O8f23IhczcnMp7Gr3MThXoKa4UpqX8PE8-1754013928-1.0.1.1-dvYa1LWEYIOon1XIBe76FZoUyz6Vu2x.8P276HUOMyVMofTDHmNu5hEhTmxUvHxFS.9gYrC1HCc9ybZ9Lw.c3NXdr9oZG_bV1HAE5JhmQPk;
+ path=/; expires=Fri, 01-Aug-25 02:35:28 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=HbUc0x4qPkKI_U32YdJwPbew_SWE.QtA5xp2OFPObCA-1754013928508-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 9681c5469bcfeb2d-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-BzZR9YT9QaqWgUpKLtvZX4c4rtHzi",
+ "object": "chat.completion",
+ "created": 1754013927,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": null,
+ "tool_calls": [
+ {
+ "id": "call_yEkR4apv4yGZGKhwfvT9s76S",
+ "type": "function",
+ "function": {
+ "name": "book",
+ "arguments": "{\"flight_id\":\"AA123\",\"passenger_name\":\"John Doe\"}"
+ }
+ }
+ ],
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "tool_calls"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 171,
+ "completion_tokens": 22,
+ "total_tokens": 193,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_62a23a81ef"
+ }
+ recorded_at: Fri, 01 Aug 2025 02:05:28 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/fixtures/vcr_cassettes/travel_agent_confirm_llm.yml b/test/fixtures/vcr_cassettes/travel_agent_confirm_llm.yml
new file mode 100644
index 00000000..0b92d974
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/travel_agent_confirm_llm.yml
@@ -0,0 +1,130 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"Confirm
+ booking TRV789012 for Jane Smith","type":"text/plain","charset":"UTF-8"}],"temperature":0.7,"tools":[{"type":"function","function":{"name":"search","description":"Search
+ for available flights to a destination","parameters":{"type":"object","properties":{"departure":{"type":"string","description":"Departure
+ city or airport code"},"destination":{"type":"string","description":"Destination
+ city or airport code"},"date":{"type":"string","description":"Travel date
+ in YYYY-MM-DD format"}},"required":["destination"]}}},{"type":"function","function":{"name":"book","description":"Book
+ a specific flight","parameters":{"type":"object","properties":{"flight_id":{"type":"string","description":"The
+ flight identifier to book"},"passenger_name":{"type":"string","description":"Name
+ of the passenger"},"passenger_email":{"type":"string","description":"Email
+ address for booking confirmation"}},"required":["flight_id","passenger_name"]}}},{"type":"function","function":{"name":"confirm","description":"Confirm
+ a flight booking","parameters":{"type":"object","properties":{"confirmation_number":{"type":"string","description":"The
+ booking confirmation number"},"send_email":{"type":"boolean","description":"Whether
+ to send confirmation email"}},"required":["confirmation_number"]}}}]}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Fri, 01 Aug 2025 02:05:27 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '692'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '1075'
+ X-Request-Id:
+ - 94da4507-5667-40c8-b6f4-4c110c19da99
+ X-Envoy-Decorator-Operation:
+ - router.openai.svc.cluster.local:5004/*
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=MUOMqvKtTTBhCh2TYCHoeORsWTrV3H_3tXzTXsqGNls-1754013927-1.0.1.1-LBRWPsx_hklicycc9.juLEpMyF05rErZr4TxCxVzqTEKBOSXO3AUGTSU3Kiq3c9ksUDPqua6gKmUr3N1dzS6K4MIQqOJrM44EH8QPt9zfHI;
+ path=/; expires=Fri, 01-Aug-25 02:35:27 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=E51nv0C58a_8YdQ4GHN2LbfB4s0xq3f.ny9JU5VE6V0-1754013927401-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 9681c53dbaff67ee-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-BzZR8gQANefBRQXj5C2bZwZIJ63sd",
+ "object": "chat.completion",
+ "created": 1754013926,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": null,
+ "tool_calls": [
+ {
+ "id": "call_IxznFlfFKjebg4MJSSe3xMKt",
+ "type": "function",
+ "function": {
+ "name": "confirm",
+ "arguments": "{\"confirmation_number\":\"TRV789012\"}"
+ }
+ }
+ ],
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "tool_calls"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 173,
+ "completion_tokens": 17,
+ "total_tokens": 190,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_62a23a81ef"
+ }
+ recorded_at: Fri, 01 Aug 2025 02:05:27 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/fixtures/vcr_cassettes/travel_agent_conversation_flow.yml b/test/fixtures/vcr_cassettes/travel_agent_conversation_flow.yml
new file mode 100644
index 00000000..27262cf9
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/travel_agent_conversation_flow.yml
@@ -0,0 +1,132 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"I
+ need to search for flights from NYC to LAX, then book one and confirm it","type":"text/plain","charset":"UTF-8"}],"temperature":0.7,"tools":[{"type":"function","function":{"name":"search","description":"Search
+ for available flights to a destination","parameters":{"type":"object","properties":{"departure":{"type":"string","description":"Departure
+ city or airport code"},"destination":{"type":"string","description":"Destination
+ city or airport code"},"date":{"type":"string","description":"Travel date
+ in YYYY-MM-DD format"}},"required":["destination"]}}},{"type":"function","function":{"name":"book","description":"Book
+ a specific flight","parameters":{"type":"object","properties":{"flight_id":{"type":"string","description":"The
+ flight identifier to book"},"passenger_name":{"type":"string","description":"Name
+ of the passenger"},"passenger_email":{"type":"string","description":"Email
+ address for booking confirmation"}},"required":["flight_id","passenger_name"]}}},{"type":"function","function":{"name":"confirm","description":"Confirm
+ a flight booking","parameters":{"type":"object","properties":{"confirmation_number":{"type":"string","description":"The
+ booking confirmation number"},"send_email":{"type":"boolean","description":"Whether
+ to send confirmation email"}},"required":["confirmation_number"]}}}]}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Fri, 01 Aug 2025 02:05:31 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '1840'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '1851'
+ X-Ratelimit-Limit-Requests:
+ - '10000'
+ X-Ratelimit-Limit-Tokens:
+ - '200000'
+ X-Ratelimit-Remaining-Requests:
+ - '9997'
+ X-Ratelimit-Remaining-Tokens:
+ - '199971'
+ X-Ratelimit-Reset-Requests:
+ - 23.627s
+ X-Ratelimit-Reset-Tokens:
+ - 8ms
+ X-Request-Id:
+ - req_a44cabd7ef424440bb3d7e3ed25ae7bd
+ X-Envoy-Decorator-Operation:
+ - router.openai.svc.cluster.local:5004/*
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=iKmYMe_gXtV5hk8GXwb25QVga.5c4h1UBFbWWSdxvA4-1754013931-1.0.1.1-CTS9lpjB8rIVcPUvoLtvRob3xXuTj5mOC4VMBN218KCDdos_hCr938OyevQVkfooaD.o6.MkgvUN8UX96zP3HHbJUTPGPgOal_Z_yh74aLM;
+ path=/; expires=Fri, 01-Aug-25 02:35:31 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=XtiGlai9Rb__JQIrcp2BnrhVuLBPtYKy1I3TtMZHNxY-1754013931882-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 9681c5563a2ef97b-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-BzZRCxe06iNw1cj5slYQV1njtwE8L",
+ "object": "chat.completion",
+ "created": 1754013930,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "Please provide me with the following details so I can assist you with searching for the flights, booking one, and confirming it:\n\n1. Desired travel date (in YYYY-MM-DD format).\n2. Name of the passenger.\n3. Email address for booking confirmation.",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 182,
+ "completion_tokens": 53,
+ "total_tokens": 235,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_62a23a81ef"
+ }
+ recorded_at: Fri, 01 Aug 2025 02:05:31 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/fixtures/vcr_cassettes/travel_agent_custom_user_instructions.yml b/test/fixtures/vcr_cassettes/travel_agent_custom_user_instructions.yml
new file mode 100644
index 00000000..9a48589a
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/travel_agent_custom_user_instructions.yml
@@ -0,0 +1,132 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"I
+ need to find a hotel in Paris","type":"text/plain","charset":"UTF-8"}],"temperature":0.7,"tools":[{"type":"function","function":{"name":"book","description":"Book
+ a specific flight","parameters":{"type":"object","properties":{"params":{"type":"object","properties":{"flight_id":{"type":"string","description":"The
+ flight identifier to book"},"passenger_name":{"type":"string","description":"Name
+ of the passenger"},"passenger_email":{"type":"string","description":"Email
+ address for booking confirmation"}},"required":["flight_id","passenger_name"]}},"required":["params"]}}},{"type":"function","function":{"name":"search","description":"Search
+ for available flights to a destination","parameters":{"type":"object","properties":{"departure":{"type":"string","description":"Departure
+ city or airport code"},"destination":{"type":"string","description":"Destination
+ city or airport code"},"date":{"type":"string","description":"Travel date
+ in YYYY-MM-DD format"}},"required":["destination"]}}},{"type":"function","function":{"name":"confirm","description":"Confirm
+ a flight booking","parameters":{"type":"object","properties":{"params":{"type":"object","properties":{"confirmation_number":{"type":"string","description":"The
+ booking confirmation number"},"send_email":{"type":"boolean","description":"Whether
+ to send confirmation email"}},"required":["confirmation_number"]}},"required":["params"]}}}]}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Sat, 02 Aug 2025 02:12:54 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '1523'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '1654'
+ X-Ratelimit-Limit-Requests:
+ - '10000'
+ X-Ratelimit-Limit-Tokens:
+ - '200000'
+ X-Ratelimit-Remaining-Requests:
+ - '9999'
+ X-Ratelimit-Remaining-Tokens:
+ - '199982'
+ X-Ratelimit-Reset-Requests:
+ - 8.64s
+ X-Ratelimit-Reset-Tokens:
+ - 5ms
+ X-Request-Id:
+ - req_c04647af78c14420853d645577fd7d34
+ X-Envoy-Decorator-Operation:
+ - router.openai.svc.cluster.local:5004/*
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=h8yNLM8BnpWSjfwDsvgBIgvvAU6hf_HDnC0C2lon7Hc-1754100774-1.0.1.1-zcp1Le4djU5.NphizJU7ooAjnxoNQH835EQpqG81EuVgk0AK1Bwh57pXu7ZkgMPZK966HzFmXn6B8HRDsBg9Hd3l4ypp.rce_KE.JbI.U4U;
+ path=/; expires=Sat, 02-Aug-25 02:42:54 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=481NfMhLSn4a4KBUQOoY7rS4xfs71rIGOCB7FKWkfPw-1754100774442-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 968a0d847aa61613-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-Bzw1sIUXPzXEvsIvKAxAOU2dipex6",
+ "object": "chat.completion",
+ "created": 1754100772,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "I currently don't have the ability to search for hotels directly. However, I can help you with information on how to find hotels in Paris or suggest popular areas and options for accommodation.\n\nWould you like tips on the best areas to stay in Paris or other hotel booking advice?",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 180,
+ "completion_tokens": 55,
+ "total_tokens": 235,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_62a23a81ef"
+ }
+ recorded_at: Sat, 02 Aug 2025 02:12:54 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/fixtures/vcr_cassettes/travel_agent_search_llm.yml b/test/fixtures/vcr_cassettes/travel_agent_search_llm.yml
new file mode 100644
index 00000000..efd55b0b
--- /dev/null
+++ b/test/fixtures/vcr_cassettes/travel_agent_search_llm.yml
@@ -0,0 +1,142 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.openai.com/v1/chat/completions
+ body:
+ encoding: UTF-8
+ string: '{"model":"gpt-4o-mini","messages":[{"role":"system","content":"You
+ are a helpful assistant.","type":"text/plain","charset":"UTF-8"},{"role":"user","content":"Find
+ flights from NYC to LAX","type":"text/plain","charset":"UTF-8"}],"temperature":0.7,"tools":[{"type":"function","function":{"name":"search","description":"Search
+ for available flights to a destination","parameters":{"type":"object","properties":{"departure":{"type":"string","description":"Departure
+ city or airport code"},"destination":{"type":"string","description":"Destination
+ city or airport code"},"date":{"type":"string","description":"Travel date
+ in YYYY-MM-DD format"}},"required":["destination"]}}},{"type":"function","function":{"name":"book","description":"Book
+ a specific flight","parameters":{"type":"object","properties":{"flight_id":{"type":"string","description":"The
+ flight identifier to book"},"passenger_name":{"type":"string","description":"Name
+ of the passenger"},"passenger_email":{"type":"string","description":"Email
+ address for booking confirmation"}},"required":["flight_id","passenger_name"]}}},{"type":"function","function":{"name":"confirm","description":"Confirm
+ a flight booking","parameters":{"type":"object","properties":{"confirmation_number":{"type":"string","description":"The
+ booking confirmation number"},"send_email":{"type":"boolean","description":"Whether
+ to send confirmation email"}},"required":["confirmation_number"]}}}]}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Date:
+ - Fri, 01 Aug 2025 02:05:29 GMT
+ Content-Type:
+ - application/json
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ Access-Control-Expose-Headers:
+ - X-Request-ID
+ Openai-Organization:
+ - user-lwlf4w2yvortlzept3wqx7li
+ Openai-Processing-Ms:
+ - '905'
+ Openai-Project:
+ - proj_pcPHiweuB88laiGDTaN3nH2M
+ Openai-Version:
+ - '2020-10-01'
+ X-Envoy-Upstream-Service-Time:
+ - '1018'
+ X-Ratelimit-Limit-Requests:
+ - '10000'
+ X-Ratelimit-Limit-Tokens:
+ - '200000'
+ X-Ratelimit-Remaining-Requests:
+ - '9998'
+ X-Ratelimit-Remaining-Tokens:
+ - '199983'
+ X-Ratelimit-Reset-Requests:
+ - 16.052s
+ X-Ratelimit-Reset-Tokens:
+ - 5ms
+ X-Request-Id:
+ - req_6419ed4e74ad7e2d2e293c66d1085fe4
+ X-Envoy-Decorator-Operation:
+ - router.openai.svc.cluster.local:5004/*
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains; preload
+ Cf-Cache-Status:
+ - DYNAMIC
+ Set-Cookie:
+ - __cf_bm=fQLc69N8evjcXZuEaXsMYSVBNaHifrpVBS7QmWi532Y-1754013929-1.0.1.1-jwXmMOz.PdCLV50LU1aE7lkxHG.QgLQp0sd26bbkOIjPtf7LFy5WKb2a0MRFyl1ACbr4AaypHpv80JuM4Pbl1CzO5Wri.iSxEdFFeFsUWsA;
+ path=/; expires=Fri, 01-Aug-25 02:35:29 GMT; domain=.api.openai.com; HttpOnly;
+ Secure; SameSite=None
+ - _cfuvid=8u94giX02MDQd2eOyxgUqQm4H7IMIQ1a7yhMlNJGSW0-1754013929906-0.0.1.1-604800000;
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
+ X-Content-Type-Options:
+ - nosniff
+ Server:
+ - cloudflare
+ Cf-Ray:
+ - 9681c54d7b3f2364-SJC
+ Alt-Svc:
+ - h3=":443"; ma=86400
+ body:
+ encoding: ASCII-8BIT
+ string: |
+ {
+ "id": "chatcmpl-BzZRBqzlgn0A1XYwlF4fGvqtl1o9M",
+ "object": "chat.completion",
+ "created": 1754013929,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": null,
+ "tool_calls": [
+ {
+ "id": "call_zzwikHl0mVcvGllveGC2HXuc",
+ "type": "function",
+ "function": {
+ "name": "search",
+ "arguments": "{\"departure\":\"NYC\",\"destination\":\"LAX\"}"
+ }
+ }
+ ],
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "tool_calls"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 171,
+ "completion_tokens": 19,
+ "total_tokens": 190,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_62a23a81ef"
+ }
+ recorded_at: Fri, 01 Aug 2025 02:05:29 GMT
+recorded_with: VCR 6.3.1
diff --git a/test/generation_provider/anthropic_provider_test.rb b/test/generation_provider/anthropic_provider_test.rb
index 35466076..86910a10 100644
--- a/test/generation_provider/anthropic_provider_test.rb
+++ b/test/generation_provider/anthropic_provider_test.rb
@@ -1,18 +1,7 @@
require "test_helper"
# Test for Anthropic Provider gem loading and configuration
-class AnthropicProviderTest < ActiveSupport::TestCase
- def setup
- @original_config = ActiveAgent.config
- @original_rails_env = ENV["RAILS_ENV"]
- end
-
- def teardown
- ActiveAgent.instance_variable_set(:@config, @original_config) if @original_config
- ENV["RAILS_ENV"] = "test"
- ActiveAgent.load_configuration(Rails.root.join("config/active_agent.yml"))
- end
-
+class AnthropicProviderTest < ActiveAgentTestCase
# Test the gem load rescue block
test "gem load rescue block provides correct error message" do
# Since we can't easily simulate the gem not being available without complex mocking,
diff --git a/test/generation_provider/open_ai_provider_test.rb b/test/generation_provider/open_ai_provider_test.rb
index 6839742f..ae332a34 100644
--- a/test/generation_provider/open_ai_provider_test.rb
+++ b/test/generation_provider/open_ai_provider_test.rb
@@ -1,18 +1,7 @@
require "test_helper"
# Test for OpenAI Provider gem loading and configuration
-class OpenAIProviderTest < ActiveSupport::TestCase
- def setup
- @original_config = ActiveAgent.config
- @original_rails_env = ENV["RAILS_ENV"]
- end
-
- def teardown
- ActiveAgent.instance_variable_set(:@config, @original_config) if @original_config
- ENV["RAILS_ENV"] = "test"
- ActiveAgent.load_configuration(Rails.root.join("config/active_agent.yml"))
- end
-
+class OpenAIProviderTest < ActiveAgentTestCase
# Test the gem load rescue block
test "gem load rescue block provides correct error message" do
# Since we can't easily simulate the gem not being available without complex mocking,
diff --git a/test/generation_provider_examples_test.rb b/test/generation_provider_examples_test.rb
new file mode 100644
index 00000000..5f64d998
--- /dev/null
+++ b/test/generation_provider_examples_test.rb
@@ -0,0 +1,82 @@
+require "test_helper"
+
+class GenerationProviderExamplesTest < ActiveAgentTestCase
+ test "provider configuration examples" do
+ # Mock configurations for providers that might not be configured
+ mock_config = {
+ "anthropic" => {
+ "service" => "Anthropic",
+ "api_key" => "test-key",
+ "model" => "claude-3-5-sonnet-20241022"
+ },
+ "openai" => {
+ "service" => "OpenAI",
+ "api_key" => "test-key",
+ "model" => "gpt-4"
+ },
+ "open_router" => {
+ "service" => "OpenRouter",
+ "api_key" => "test-key",
+ "model" => "anthropic/claude-3-5-sonnet"
+ }
+ }
+
+ with_active_agent_config(mock_config) do
+ # These are documentation examples only
+ # region anthropic_provider_example
+ class AnthropicConfigAgent < ActiveAgent::Base
+ generate_with :anthropic,
+ model: "claude-3-5-sonnet-20241022",
+ temperature: 0.7
+ end
+ # endregion anthropic_provider_example
+
+ # region open_router_provider_example
+ class OpenRouterConfigAgent < ActiveAgent::Base
+ generate_with :open_router,
+ model: "anthropic/claude-3-5-sonnet",
+ temperature: 0.5
+ end
+ # endregion open_router_provider_example
+
+ # region custom_host_configuration
+ class CustomHostAgent < ActiveAgent::Base
+ generate_with :openai,
+ host: "https://your-azure-openai-resource.openai.azure.com",
+ api_key: "your-api-key",
+ model: "gpt-4"
+ end
+ # endregion custom_host_configuration
+
+ assert_equal "anthropic", AnthropicConfigAgent.generation_provider_name
+ assert_equal "open_router", OpenRouterConfigAgent.generation_provider_name
+ assert_equal "openai", CustomHostAgent.generation_provider_name
+ end
+ end
+
+ test "response object usage" do
+ VCR.use_cassette("generation_response_usage_example") do
+ # region generation_response_usage
+ response = ApplicationAgent.with(message: "Hello").prompt_context.generate_now
+
+ # Access response content
+ content = response.message.content
+
+ # Access response role
+ role = response.message.role
+
+ # Access full prompt context
+ messages = response.prompt.messages
+
+ # Access usage statistics (if available)
+ usage = response.usage
+ # endregion generation_response_usage
+
+ doc_example_output(response)
+
+ assert_not_nil content
+ assert_equal :assistant, role
+ assert messages.is_a?(Array)
+ end
+ end
+end
diff --git a/test/option_hierarchy_test.rb b/test/option_hierarchy_test.rb
index 3925b601..c7503480 100644
--- a/test/option_hierarchy_test.rb
+++ b/test/option_hierarchy_test.rb
@@ -23,8 +23,10 @@ def custom_template_action
prompt = agent.prompt(
message: "test",
- temperature: 0.9,
- model: "gpt-3.5-turbo"
+ options: {
+ temperature: 0.9,
+ model: "gpt-3.5-turbo"
+ }
)
assert_equal "gpt-3.5-turbo", prompt.options[:model]
@@ -58,11 +60,15 @@ def custom_template_action
test "with method supports runtime options via options parameter" do
test_agent_class = create_test_agent
+ # region runtime_options_with_method
prompt = test_agent_class.with(
message: "test",
- temperature: 0.8,
- model: "gpt-4o"
+ options: {
+ temperature: 0.8,
+ model: "gpt-4o"
+ }
).prompt_context
+ # endregion runtime_options_with_method
assert_equal "test", prompt.message.content
assert_equal 0.8, prompt.options[:temperature]
@@ -77,11 +83,10 @@ def custom_template_action
prompt = agent.prompt(
message: "test",
options: { temperature: 0.6, model: "gpt-3.5-turbo" },
- temperature: 0.9 # This should override the options parameter
)
# Direct prompt options should override options parameter
- assert_equal 0.9, prompt.options[:temperature]
+ assert_equal 0.6, prompt.options[:temperature]
assert_equal "gpt-3.5-turbo", prompt.options[:model] # from options param
end
@@ -142,8 +147,10 @@ def custom_template_action
test_agent_class.with(
message: "Hello",
custom_param: "not_a_runtime_option",
- temperature: 0.8,
- model: "gpt-4o"
+ options: {
+ temperature: 0.8,
+ model: "gpt-4o"
+ }
).tap do |agent_with_options|
# Verify params contain both runtime options and regular params
params = agent_with_options.instance_variable_get(:@params)
@@ -161,22 +168,29 @@ def custom_template_action
test "different runtime option types are supported" do
test_agent_class = create_test_agent
- prompt = test_agent_class.with(
+ # region runtime_options_types
+ parameterized_agent = test_agent_class.with(
message: "test",
- top_p: 0.95,
- frequency_penalty: 0.1,
- presence_penalty: 0.2,
- seed: 12345,
- stop: [ "END" ],
+ options: {
+ temperature: 0.8,
+ model: "gpt-4o",
+ top_p: 0.95,
+ frequency_penalty: 0.1,
+ presence_penalty: 0.2,
+ seed: 12345,
+ stop: [ "END" ]
+ },
user: "test-user"
- ).prompt_context
+ )
+
+ prompt = parameterized_agent.prompt_context
+ # endregion runtime_options_types
assert_equal 0.95, prompt.options[:top_p]
assert_equal 0.1, prompt.options[:frequency_penalty]
assert_equal 0.2, prompt.options[:presence_penalty]
assert_equal 12345, prompt.options[:seed]
assert_equal [ "END" ], prompt.options[:stop]
- assert_equal "test-user", prompt.options[:user]
end
test "template path and name can be overridden separately" do
@@ -199,6 +213,7 @@ def custom_template_action
test_agent_class = create_test_agent
agent = test_agent_class.new
+ # region runtime_options_in_prompt
# Explicit options via :options parameter
prompt = agent.prompt(
message: "test",
@@ -206,12 +221,35 @@ def custom_template_action
temperature: 0.6,
model: "claude-3",
max_tokens: 2000
- },
- temperature: 0.9 # This should override the options hash
+ }
)
+ # endregion runtime_options_in_prompt
- assert_equal 0.9, prompt.options[:temperature] # Direct param wins
+ assert_equal 0.6, prompt.options[:temperature] # Direct param wins
assert_equal "claude-3", prompt.options[:model] # From options hash
assert_equal 2000, prompt.options[:max_tokens] # From options hash
end
+
+ test "runtime options example output" do
+ test_agent_class = create_test_agent
+
+ # Example of using runtime options with the with method
+ prompt = test_agent_class.with(
+ message: "Translate 'Hello' to Spanish",
+ options: {
+ temperature: 0.7,
+ model: "gpt-4o",
+ max_tokens: 100
+ }
+ ).prompt_context
+
+ doc_example_output({
+ prompt_options: prompt.options,
+ message: prompt.message.content
+ })
+ assert_equal "Translate 'Hello' to Spanish", prompt.message.content
+ assert_equal 0.7, prompt.options[:temperature]
+ assert_equal "gpt-4o", prompt.options[:model]
+ assert_equal 100, prompt.options[:max_tokens]
+ end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 36a85636..feced41a 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -2,13 +2,92 @@
ENV["RAILS_ENV"] = "test"
require "jbuilder"
-require "active_agent/action_prompt"
require_relative "../test/dummy/config/environment"
ActiveRecord::Migrator.migrations_paths = [ File.expand_path("../test/dummy/db/migrate", __dir__) ]
require "rails/test_help"
require "vcr"
require "minitest/mock"
+# Extract full path and relative path from caller_info
+def extract_path_info(caller_info)
+ if caller_info =~ /(.+):(\d+):in/
+ full_path = $1
+ line_number = $2
+
+ # Get relative path from project root
+ project_root = File.expand_path("../..", __dir__)
+ relative_path = full_path.gsub(project_root + "/", "")
+
+ {
+ full_path: full_path,
+ relative_path: relative_path,
+ line_number: line_number,
+ file_name: File.basename(full_path)
+ }
+ else
+ {}
+ end
+end
+
+def doc_example_output(example = nil, test_name = nil)
+ # Extract caller information
+ caller_info = caller.find { |line| line.include?("_test.rb") }
+ file_name = @NAME.dasherize
+ test_name ||= name.to_s.dasherize if respond_to?(:name)
+
+ # Extract file path and line number from caller
+ if caller_info =~ /(.+):(\d+):in/
+ test_file = $1.split("/").last
+ line_number = $2
+ end
+
+ path_info = extract_path_info(caller_info)
+
+ file_path = Rails.root.join("..", "..", "docs", "parts", "examples", "#{file_name}-#{test_name}.md")
+ # puts "\nWriting example output to #{file_path}\n"
+ FileUtils.mkdir_p(File.dirname(file_path))
+
+ open_local = "vscode://file/#{path_info[:full_path]}:#{path_info[:line_number]}"
+
+ open_remote = "https://github.com/activeagents/activeagent/tree/main/test#{path_info[:relative_path]}:#{path_info[:line_number]}"
+
+ open_link = ENV["GITHUB_ACTIONS"] ? open_remote : open_local
+
+ # Format the output with metadata
+ content = []
+ content << ""
+
+ content << "[#{path_info[:relative_path]}:#{path_info[:line_number]}](#{open_link})"
+ content << ""
+ content << ""
+
+ # Determine if example is JSON
+ if example.is_a?(Hash) || example.is_a?(Array)
+ content << "```json"
+ content << JSON.pretty_generate(example)
+ content << "```"
+ elsif example.respond_to?(:message) && example.respond_to?(:prompt)
+ # Handle response objects
+ content << "```ruby"
+ content << "# Response object"
+ content << "#<#{example.class.name}:0x#{example.object_id.to_s(16)}"
+ content << " @message=#{example.message.inspect}"
+ content << " @prompt=#<#{example.prompt.class.name}:0x#{example.prompt.object_id.to_s(16)} ...>"
+ content << " @content_type=#{example.message.content_type.inspect}"
+ content << " @raw_response={...}>"
+ content << ""
+ content << "# Message content"
+ content << "response.message.content # => #{example.message.content.inspect}"
+ content << "```"
+ else
+ content << "```ruby"
+ content << ActiveAgent.filter_credential_keys(example.to_s)
+ content << "```"
+ end
+
+ File.write(file_path, content.join("\n"))
+end
+
VCR.configure do |config|
config.cassette_library_dir = "test/fixtures/vcr_cassettes"
config.hook_into :webmock
@@ -23,3 +102,34 @@
ActiveSupport::TestCase.file_fixture_path = File.expand_path("test/fixtures", __dir__) + "/files"
ActiveSupport::TestCase.fixtures :all
end
+
+# Base test case that properly manages ActiveAgent configuration
+class ActiveAgentTestCase < ActiveSupport::TestCase
+ def setup
+ super
+ # Store original configuration
+ @original_config = ActiveAgent.config.dup if ActiveAgent.config
+ @original_rails_env = ENV["RAILS_ENV"]
+ # Ensure we're in test environment
+ ENV["RAILS_ENV"] = "test"
+ end
+
+ def teardown
+ super
+ # Restore original configuration
+ ActiveAgent.instance_variable_set(:@config, @original_config) if @original_config
+ ENV["RAILS_ENV"] = @original_rails_env
+ # Reload default configuration
+ config_file = Rails.root.join("config/active_agent.yml")
+ ActiveAgent.load_configuration(config_file) if File.exist?(config_file)
+ end
+
+ # Helper method to temporarily set configuration
+ def with_active_agent_config(config)
+ old_config = ActiveAgent.config
+ ActiveAgent.instance_variable_set(:@config, config)
+ yield
+ ensure
+ ActiveAgent.instance_variable_set(:@config, old_config)
+ end
+end
diff --git a/test/tool_configuration_test.rb b/test/tool_configuration_test.rb
new file mode 100644
index 00000000..2ac877dd
--- /dev/null
+++ b/test/tool_configuration_test.rb
@@ -0,0 +1,89 @@
+require "test_helper"
+
+class ToolConfigurationTest < ActiveSupport::TestCase
+ # region tool_implementation_example
+ class ToolExampleAgent < ApplicationAgent
+ def get_weather
+ # Tool implementation
+ {
+ temperature: 72,
+ condition: "Sunny",
+ location: params[:location] || "New York"
+ }
+ end
+
+ def search_products
+ # Another tool implementation
+ query = params[:query]
+ [
+ { name: "Product A", price: 29.99 },
+ { name: "Product B", price: 39.99 }
+ ]
+ end
+ end
+ # endregion tool_implementation_example
+
+ # region tool_configuration_example
+ class ConfiguredToolAgent < ApplicationAgent
+ def analyze_with_tools
+ prompt.tools = [
+ {
+ name: "get_weather",
+ description: "Get current weather for a location",
+ parameters: {
+ type: "object",
+ properties: {
+ location: {
+ type: "string",
+ description: "City name"
+ }
+ },
+ required: [ "location" ]
+ }
+ },
+ {
+ name: "search_products",
+ description: "Search for products",
+ parameters: {
+ type: "object",
+ properties: {
+ query: {
+ type: "string",
+ description: "Search query"
+ }
+ },
+ required: [ "query" ]
+ }
+ }
+ ]
+ prompt
+ end
+ end
+ # endregion tool_configuration_example
+
+ test "tool implementation returns expected data" do
+ agent = ToolExampleAgent.new
+ weather = agent.get_weather
+
+ assert_equal 72, weather[:temperature]
+ assert_equal "Sunny", weather[:condition]
+ assert_equal "New York", weather[:location]
+ end
+
+ test "tool configuration sets tools correctly" do
+ agent = ConfiguredToolAgent.new
+ agent.params = { message: "Test tools" }
+
+ # The analyze_with_tools method configures tools on the prompt
+ # We'll test that the method exists and can be called
+ assert_respond_to agent, :analyze_with_tools
+
+ # For documentation purposes, show tool configuration
+ # The actual prompt.tools would be set when called
+ expected_tools = [
+ { name: "get_weather", description: "Get current weather for a location" },
+ { name: "search_products", description: "Search for products" }
+ ]
+ assert_equal 2, expected_tools.length
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index 9315aebd..84ff49f1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3,36 +3,36 @@
"@algolia/autocomplete-core@1.17.7":
- "integrity" "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="
- "resolved" "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz"
- "version" "1.17.7"
+ version "1.17.7"
+ resolved "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz"
+ integrity sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==
dependencies:
"@algolia/autocomplete-plugin-algolia-insights" "1.17.7"
"@algolia/autocomplete-shared" "1.17.7"
"@algolia/autocomplete-plugin-algolia-insights@1.17.7":
- "integrity" "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A=="
- "resolved" "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz"
- "version" "1.17.7"
+ version "1.17.7"
+ resolved "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz"
+ integrity sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==
dependencies:
"@algolia/autocomplete-shared" "1.17.7"
"@algolia/autocomplete-preset-algolia@1.17.7":
- "integrity" "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA=="
- "resolved" "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz"
- "version" "1.17.7"
+ version "1.17.7"
+ resolved "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz"
+ integrity sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==
dependencies:
"@algolia/autocomplete-shared" "1.17.7"
"@algolia/autocomplete-shared@1.17.7":
- "integrity" "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="
- "resolved" "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz"
- "version" "1.17.7"
+ version "1.17.7"
+ resolved "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz"
+ integrity sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==
"@algolia/client-abtesting@5.27.0":
- "integrity" "sha512-SITU5umoknxETtw67TxJu9njyMkWiH8pM+Bvw4dzfuIrIAT6Y1rmwV4y0A0didWoT+6xVuammIykbtBMolBcmg=="
- "resolved" "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.27.0.tgz"
+ integrity sha512-SITU5umoknxETtw67TxJu9njyMkWiH8pM+Bvw4dzfuIrIAT6Y1rmwV4y0A0didWoT+6xVuammIykbtBMolBcmg==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -40,9 +40,9 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/client-analytics@5.27.0":
- "integrity" "sha512-go1b9qIZK5vYEQ7jD2bsfhhhVsoh9cFxQ5xF8TzTsg2WOCZR3O92oXCkq15SOK0ngJfqDU6a/k0oZ4KuEnih1Q=="
- "resolved" "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.27.0.tgz"
+ integrity sha512-go1b9qIZK5vYEQ7jD2bsfhhhVsoh9cFxQ5xF8TzTsg2WOCZR3O92oXCkq15SOK0ngJfqDU6a/k0oZ4KuEnih1Q==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -50,14 +50,14 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/client-common@5.27.0":
- "integrity" "sha512-tnFOzdNuMzsz93kOClj3fKfuYoF3oYaEB5bggULSj075GJ7HUNedBEm7a6ScrjtnOaOtipbnT7veUpHA4o4wEQ=="
- "resolved" "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.27.0.tgz"
+ integrity sha512-tnFOzdNuMzsz93kOClj3fKfuYoF3oYaEB5bggULSj075GJ7HUNedBEm7a6ScrjtnOaOtipbnT7veUpHA4o4wEQ==
"@algolia/client-insights@5.27.0":
- "integrity" "sha512-y1qgw39qZijjQBXrqZTiwK1cWgWGRiLpJNWBv9w36nVMKfl9kInrfsYmdBAfmlhVgF/+Woe0y1jQ7pa4HyShAw=="
- "resolved" "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.27.0.tgz"
+ integrity sha512-y1qgw39qZijjQBXrqZTiwK1cWgWGRiLpJNWBv9w36nVMKfl9kInrfsYmdBAfmlhVgF/+Woe0y1jQ7pa4HyShAw==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -65,9 +65,9 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/client-personalization@5.27.0":
- "integrity" "sha512-XluG9qPZKEbiLoIfXTKbABsWDNOMPx0t6T2ImJTTeuX+U/zBdmfcqqgcgkqXp+vbXof/XX/4of9Eqo1JaqEmKw=="
- "resolved" "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.27.0.tgz"
+ integrity sha512-XluG9qPZKEbiLoIfXTKbABsWDNOMPx0t6T2ImJTTeuX+U/zBdmfcqqgcgkqXp+vbXof/XX/4of9Eqo1JaqEmKw==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -75,19 +75,19 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/client-query-suggestions@5.27.0":
- "integrity" "sha512-V8/To+SsAl2sdw2AAjeLJuCW1L+xpz+LAGerJK7HKqHzE5yQhWmIWZTzqYQcojkii4iBMYn0y3+uReWqT8XVSQ=="
- "resolved" "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.27.0.tgz"
+ integrity sha512-V8/To+SsAl2sdw2AAjeLJuCW1L+xpz+LAGerJK7HKqHzE5yQhWmIWZTzqYQcojkii4iBMYn0y3+uReWqT8XVSQ==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
"@algolia/requester-fetch" "5.27.0"
"@algolia/requester-node-http" "5.27.0"
-"@algolia/client-search@>= 4.9.1 < 6", "@algolia/client-search@5.27.0":
- "integrity" "sha512-EJJ7WmvmUXZdchueKFCK8UZFyLqy4Hz64snNp0cTc7c0MKaSeDGYEDxVsIJKp15r7ORaoGxSyS4y6BGZMXYuCg=="
- "resolved" "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.27.0.tgz"
- "version" "5.27.0"
+"@algolia/client-search@5.27.0":
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.27.0.tgz"
+ integrity sha512-EJJ7WmvmUXZdchueKFCK8UZFyLqy4Hz64snNp0cTc7c0MKaSeDGYEDxVsIJKp15r7ORaoGxSyS4y6BGZMXYuCg==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -95,9 +95,9 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/ingestion@1.27.0":
- "integrity" "sha512-xNCyWeqpmEo4EdmpG57Fs1fJIQcPwt5NnJ6MBdXnUdMVXF4f5PHgza+HQWQQcYpCsune96jfmR0v7us6gRIlCw=="
- "resolved" "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.27.0.tgz"
- "version" "1.27.0"
+ version "1.27.0"
+ resolved "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.27.0.tgz"
+ integrity sha512-xNCyWeqpmEo4EdmpG57Fs1fJIQcPwt5NnJ6MBdXnUdMVXF4f5PHgza+HQWQQcYpCsune96jfmR0v7us6gRIlCw==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -105,9 +105,9 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/monitoring@1.27.0":
- "integrity" "sha512-P0NDiEFyt9UYQLBI0IQocIT7xHpjMpoFN3UDeerbztlkH9HdqT0GGh1SHYmNWpbMWIGWhSJTtz6kSIWvFu4+pw=="
- "resolved" "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.27.0.tgz"
- "version" "1.27.0"
+ version "1.27.0"
+ resolved "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.27.0.tgz"
+ integrity sha512-P0NDiEFyt9UYQLBI0IQocIT7xHpjMpoFN3UDeerbztlkH9HdqT0GGh1SHYmNWpbMWIGWhSJTtz6kSIWvFu4+pw==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -115,9 +115,9 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/recommend@5.27.0":
- "integrity" "sha512-cqfTMF1d1cc7hg0vITNAFxJZas7MJ4Obc36WwkKpY23NOtGb+4tH9X7UKlQa2PmTgbXIANoJ/DAQTeiVlD2I4Q=="
- "resolved" "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.27.0.tgz"
+ integrity sha512-cqfTMF1d1cc7hg0vITNAFxJZas7MJ4Obc36WwkKpY23NOtGb+4tH9X7UKlQa2PmTgbXIANoJ/DAQTeiVlD2I4Q==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-browser-xhr" "5.27.0"
@@ -125,410 +125,615 @@
"@algolia/requester-node-http" "5.27.0"
"@algolia/requester-browser-xhr@5.27.0":
- "integrity" "sha512-ErenYTcXl16wYXtf0pxLl9KLVxIztuehqXHfW9nNsD8mz9OX42HbXuPzT7y6JcPiWJpc/UU/LY5wBTB65vsEUg=="
- "resolved" "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.27.0.tgz"
+ integrity sha512-ErenYTcXl16wYXtf0pxLl9KLVxIztuehqXHfW9nNsD8mz9OX42HbXuPzT7y6JcPiWJpc/UU/LY5wBTB65vsEUg==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-fetch@5.27.0":
- "integrity" "sha512-CNOvmXsVi+IvT7z1d+6X7FveVkgEQwTNgipjQCHTIbF9KSMfZR7tUsJC+NpELrm10ALdOMauah84ybs9rw1cKQ=="
- "resolved" "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.27.0.tgz"
+ integrity sha512-CNOvmXsVi+IvT7z1d+6X7FveVkgEQwTNgipjQCHTIbF9KSMfZR7tUsJC+NpELrm10ALdOMauah84ybs9rw1cKQ==
dependencies:
"@algolia/client-common" "5.27.0"
"@algolia/requester-node-http@5.27.0":
- "integrity" "sha512-Nx9EdLYZDsaYFTthqmc0XcVvsx6jqeEX8fNiYOB5i2HboQwl8pJPj1jFhGqoGd0KG7KFR+sdPO5/e0EDDAru2Q=="
- "resolved" "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.27.0.tgz"
- "version" "5.27.0"
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.27.0.tgz"
+ integrity sha512-Nx9EdLYZDsaYFTthqmc0XcVvsx6jqeEX8fNiYOB5i2HboQwl8pJPj1jFhGqoGd0KG7KFR+sdPO5/e0EDDAru2Q==
dependencies:
"@algolia/client-common" "5.27.0"
"@antfu/install-pkg@^1.0.0":
- "integrity" "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="
- "resolved" "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz"
- "version" "1.1.0"
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz"
+ integrity sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==
dependencies:
- "package-manager-detector" "^1.3.0"
- "tinyexec" "^1.0.1"
+ package-manager-detector "^1.3.0"
+ tinyexec "^1.0.1"
"@antfu/utils@^8.1.0":
- "integrity" "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="
- "resolved" "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz"
- "version" "8.1.1"
+ version "8.1.1"
+ resolved "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz"
+ integrity sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==
"@babel/helper-string-parser@^7.27.1":
- "integrity" "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
- "resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz"
- "version" "7.27.1"
+ version "7.27.1"
+ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz"
+ integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.27.1":
- "integrity" "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
- "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz"
- "version" "7.27.1"
+ version "7.27.1"
+ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz"
+ integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
"@babel/parser@^7.27.2":
- "integrity" "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg=="
- "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz"
- "version" "7.27.5"
+ version "7.27.5"
+ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz"
+ integrity sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==
dependencies:
"@babel/types" "^7.27.3"
"@babel/types@^7.27.3":
- "integrity" "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q=="
- "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz"
- "version" "7.27.6"
+ version "7.27.6"
+ resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz"
+ integrity sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.27.1"
"@docsearch/css@3.8.2":
- "integrity" "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ=="
- "resolved" "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz"
- "version" "3.8.2"
+ version "3.8.2"
+ resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz"
+ integrity sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==
"@docsearch/js@3.8.2":
- "integrity" "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ=="
- "resolved" "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz"
- "version" "3.8.2"
+ version "3.8.2"
+ resolved "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz"
+ integrity sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==
dependencies:
"@docsearch/react" "3.8.2"
- "preact" "^10.0.0"
+ preact "^10.0.0"
"@docsearch/react@3.8.2":
- "integrity" "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="
- "resolved" "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz"
- "version" "3.8.2"
+ version "3.8.2"
+ resolved "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz"
+ integrity sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==
dependencies:
"@algolia/autocomplete-core" "1.17.7"
"@algolia/autocomplete-preset-algolia" "1.17.7"
"@docsearch/css" "3.8.2"
- "algoliasearch" "^5.14.2"
+ algoliasearch "^5.14.2"
+
+"@esbuild/aix-ppc64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
+ integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
+
+"@esbuild/android-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
+ integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
+
+"@esbuild/android-arm@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
+ integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
+
+"@esbuild/android-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
+ integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
"@esbuild/darwin-arm64@0.21.5":
- "integrity" "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="
- "resolved" "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz"
- "version" "0.21.5"
+ version "0.21.5"
+ resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz"
+ integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
+
+"@esbuild/darwin-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
+ integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
+
+"@esbuild/freebsd-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
+ integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
+
+"@esbuild/freebsd-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
+ integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
+
+"@esbuild/linux-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
+ integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
+
+"@esbuild/linux-arm@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
+ integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
+
+"@esbuild/linux-ia32@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
+ integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
+
+"@esbuild/linux-loong64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
+ integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
+
+"@esbuild/linux-mips64el@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
+ integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
+
+"@esbuild/linux-ppc64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
+ integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
+
+"@esbuild/linux-riscv64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
+ integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
+
+"@esbuild/linux-s390x@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
+ integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
+
+"@esbuild/linux-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
+ integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
+
+"@esbuild/netbsd-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
+ integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
+
+"@esbuild/openbsd-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
+ integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
+
+"@esbuild/sunos-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
+ integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
+
+"@esbuild/win32-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
+ integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
+
+"@esbuild/win32-ia32@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
+ integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
+
+"@esbuild/win32-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
+ integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
"@iconify-json/logos@^1.2.4":
- "integrity" "sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw=="
- "resolved" "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.2.4.tgz"
- "version" "1.2.4"
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.2.4.tgz"
+ integrity sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==
dependencies:
"@iconify/types" "*"
"@iconify-json/simple-icons@^1.2.21":
- "integrity" "sha512-jZwTBznpYVDYKWyAuRpepPpCiHScVrX6f8WRX8ReX6pdii99LYVHwJywKcH2excWQrWmBomC9nkxGlEKzXZ/wQ=="
- "resolved" "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.37.tgz"
- "version" "1.2.37"
+ version "1.2.37"
+ resolved "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.37.tgz"
+ integrity sha512-jZwTBznpYVDYKWyAuRpepPpCiHScVrX6f8WRX8ReX6pdii99LYVHwJywKcH2excWQrWmBomC9nkxGlEKzXZ/wQ==
dependencies:
"@iconify/types" "*"
"@iconify-json/vscode-icons@^1.2.18":
- "integrity" "sha512-xuWqr/SrckUoFi6kpSH/NrNGK+CuZ8LNnBY8qkRdkQvHmhirXvwsLfTKHoFndTsOlxfsHahlOLVCCb523kdqMA=="
- "resolved" "https://registry.npmjs.org/@iconify-json/vscode-icons/-/vscode-icons-1.2.20.tgz"
- "version" "1.2.20"
+ version "1.2.20"
+ resolved "https://registry.npmjs.org/@iconify-json/vscode-icons/-/vscode-icons-1.2.20.tgz"
+ integrity sha512-xuWqr/SrckUoFi6kpSH/NrNGK+CuZ8LNnBY8qkRdkQvHmhirXvwsLfTKHoFndTsOlxfsHahlOLVCCb523kdqMA==
dependencies:
"@iconify/types" "*"
"@iconify/types@*", "@iconify/types@^2.0.0":
- "integrity" "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="
- "resolved" "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz"
- "version" "2.0.0"
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz"
+ integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==
"@iconify/utils@^2.3.0":
- "integrity" "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA=="
- "resolved" "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz"
- "version" "2.3.0"
+ version "2.3.0"
+ resolved "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz"
+ integrity sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==
dependencies:
"@antfu/install-pkg" "^1.0.0"
"@antfu/utils" "^8.1.0"
"@iconify/types" "^2.0.0"
- "debug" "^4.4.0"
- "globals" "^15.14.0"
- "kolorist" "^1.8.0"
- "local-pkg" "^1.0.0"
- "mlly" "^1.7.4"
+ debug "^4.4.0"
+ globals "^15.14.0"
+ kolorist "^1.8.0"
+ local-pkg "^1.0.0"
+ mlly "^1.7.4"
"@jridgewell/sourcemap-codec@^1.5.0":
- "integrity" "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
- "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
- "version" "1.5.0"
+ version "1.5.0"
+ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
+ integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
-"@rollup/rollup-darwin-arm64@4.42.0":
- "integrity" "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA=="
- "resolved" "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz"
- "version" "4.42.0"
+"@rollup/rollup-android-arm-eabi@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz#8baae15a6a27f18b7c5be420e00ab08c7d3dd6f4"
+ integrity sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==
-"@shikijs/core@^2.1.0", "@shikijs/core@2.5.0":
- "integrity" "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="
- "resolved" "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz"
- "version" "2.5.0"
+"@rollup/rollup-android-arm64@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz#6798394241d1b26f8b44d2bbd8de9c12eb9dd6e6"
+ integrity sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==
+
+"@rollup/rollup-darwin-arm64@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz"
+ integrity sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==
+
+"@rollup/rollup-darwin-x64@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz#e15568b2fea4fdc526e86424150df9ec511fbaaf"
+ integrity sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==
+
+"@rollup/rollup-freebsd-arm64@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz#0ebdb3b470ccf6acf0eacae8177f34e27477559f"
+ integrity sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==
+
+"@rollup/rollup-freebsd-x64@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz#675808bf4fe7c7fc454326510ab3be0857626d41"
+ integrity sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz#05e881cc69f59415fe8c1af13554c60c7c49d114"
+ integrity sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==
+
+"@rollup/rollup-linux-arm-musleabihf@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz#eb990bf7c3c37749c3d5afed34e6adec1c927963"
+ integrity sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==
+
+"@rollup/rollup-linux-arm64-gnu@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz#3deeacff589e7f370aca5cef29d68d4c8fa0033c"
+ integrity sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==
+
+"@rollup/rollup-linux-arm64-musl@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz#6db81ab065ef278faf83d875c77ff9cdd51abcfd"
+ integrity sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==
+
+"@rollup/rollup-linux-loongarch64-gnu@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz#90d35336ad4cbf318648e41b0e7ce3920c28ebc9"
+ integrity sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz#6d21a0f18262648ec181fc9326b8f0ac02aa744d"
+ integrity sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==
+
+"@rollup/rollup-linux-riscv64-gnu@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz#e46e2d1125957694bfb5222ecd63dd6c9bd69682"
+ integrity sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==
+
+"@rollup/rollup-linux-riscv64-musl@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz#478a23f0fa0d832a0a6fa858a9f3d2eb201d44de"
+ integrity sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==
+
+"@rollup/rollup-linux-s390x-gnu@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz#4261c714cd750e3fb685a330dfca7bb8f5711469"
+ integrity sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==
+
+"@rollup/rollup-linux-x64-gnu@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz#45aa751bdf05ac696da417a37fdfd13f607e1fab"
+ integrity sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==
+
+"@rollup/rollup-linux-x64-musl@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz#9a0f8691dede53d1720ebb2aeef72e483cf69220"
+ integrity sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==
+
+"@rollup/rollup-win32-arm64-msvc@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz#395ad8b6b6372a3888d2e96bf6c45392be815f4d"
+ integrity sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==
+
+"@rollup/rollup-win32-ia32-msvc@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz#0d80305a14fff372ea5e90cd35c63c6b8efbd143"
+ integrity sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==
+
+"@rollup/rollup-win32-x64-msvc@4.42.0":
+ version "4.42.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz#516c6770ba15fe6aef369d217a9747492c01e8b7"
+ integrity sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==
+
+"@shikijs/core@2.5.0", "@shikijs/core@^2.1.0":
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz"
+ integrity sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==
dependencies:
"@shikijs/engine-javascript" "2.5.0"
"@shikijs/engine-oniguruma" "2.5.0"
"@shikijs/types" "2.5.0"
"@shikijs/vscode-textmate" "^10.0.2"
"@types/hast" "^3.0.4"
- "hast-util-to-html" "^9.0.4"
+ hast-util-to-html "^9.0.4"
"@shikijs/engine-javascript@2.5.0":
- "integrity" "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="
- "resolved" "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz"
- "version" "2.5.0"
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz"
+ integrity sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==
dependencies:
"@shikijs/types" "2.5.0"
"@shikijs/vscode-textmate" "^10.0.2"
- "oniguruma-to-es" "^3.1.0"
+ oniguruma-to-es "^3.1.0"
"@shikijs/engine-oniguruma@2.5.0":
- "integrity" "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="
- "resolved" "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz"
- "version" "2.5.0"
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz"
+ integrity sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==
dependencies:
"@shikijs/types" "2.5.0"
"@shikijs/vscode-textmate" "^10.0.2"
"@shikijs/langs@2.5.0":
- "integrity" "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="
- "resolved" "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz"
- "version" "2.5.0"
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz"
+ integrity sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==
dependencies:
"@shikijs/types" "2.5.0"
"@shikijs/themes@2.5.0":
- "integrity" "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="
- "resolved" "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz"
- "version" "2.5.0"
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz"
+ integrity sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==
dependencies:
"@shikijs/types" "2.5.0"
"@shikijs/transformers@^2.1.0":
- "integrity" "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="
- "resolved" "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz"
- "version" "2.5.0"
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz"
+ integrity sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==
dependencies:
"@shikijs/core" "2.5.0"
"@shikijs/types" "2.5.0"
-"@shikijs/types@^2.1.0", "@shikijs/types@2.5.0":
- "integrity" "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="
- "resolved" "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz"
- "version" "2.5.0"
+"@shikijs/types@2.5.0", "@shikijs/types@^2.1.0":
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz"
+ integrity sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==
dependencies:
"@shikijs/vscode-textmate" "^10.0.2"
"@types/hast" "^3.0.4"
"@shikijs/vscode-textmate@^10.0.2":
- "integrity" "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="
- "resolved" "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz"
- "version" "10.0.2"
+ version "10.0.2"
+ resolved "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz"
+ integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==
"@types/estree@1.0.7":
- "integrity" "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
- "resolved" "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz"
- "version" "1.0.7"
+ version "1.0.7"
+ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz"
+ integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==
"@types/hast@^3.0.0", "@types/hast@^3.0.4":
- "integrity" "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="
- "resolved" "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz"
- "version" "3.0.4"
+ version "3.0.4"
+ resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz"
+ integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
dependencies:
"@types/unist" "*"
"@types/linkify-it@^5":
- "integrity" "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="
- "resolved" "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz"
- "version" "5.0.0"
+ version "5.0.0"
+ resolved "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz"
+ integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==
"@types/markdown-it@^14.1.2":
- "integrity" "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="
- "resolved" "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz"
- "version" "14.1.2"
+ version "14.1.2"
+ resolved "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz"
+ integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==
dependencies:
"@types/linkify-it" "^5"
"@types/mdurl" "^2"
"@types/mdast@^4.0.0":
- "integrity" "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="
- "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz"
- "version" "4.0.4"
+ version "4.0.4"
+ resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz"
+ integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==
dependencies:
"@types/unist" "*"
"@types/mdurl@^2":
- "integrity" "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="
- "resolved" "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz"
- "version" "2.0.0"
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz"
+ integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==
"@types/unist@*", "@types/unist@^3.0.0":
- "integrity" "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
- "resolved" "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz"
- "version" "3.0.3"
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz"
+ integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
"@types/web-bluetooth@^0.0.21":
- "integrity" "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="
- "resolved" "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz"
- "version" "0.0.21"
+ version "0.0.21"
+ resolved "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz"
+ integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==
"@ungap/structured-clone@^1.0.0":
- "integrity" "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="
- "resolved" "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
- "version" "1.3.0"
+ version "1.3.0"
+ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
+ integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
"@vitejs/plugin-vue@^5.2.1":
- "integrity" "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="
- "resolved" "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz"
- "version" "5.2.4"
+ version "5.2.4"
+ resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz"
+ integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==
"@vue/compiler-core@3.5.16":
- "integrity" "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ=="
- "resolved" "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz"
+ integrity sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==
dependencies:
"@babel/parser" "^7.27.2"
"@vue/shared" "3.5.16"
- "entities" "^4.5.0"
- "estree-walker" "^2.0.2"
- "source-map-js" "^1.2.1"
+ entities "^4.5.0"
+ estree-walker "^2.0.2"
+ source-map-js "^1.2.1"
"@vue/compiler-dom@3.5.16":
- "integrity" "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ=="
- "resolved" "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz"
+ integrity sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==
dependencies:
"@vue/compiler-core" "3.5.16"
"@vue/shared" "3.5.16"
"@vue/compiler-sfc@3.5.16":
- "integrity" "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw=="
- "resolved" "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz"
+ integrity sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==
dependencies:
"@babel/parser" "^7.27.2"
"@vue/compiler-core" "3.5.16"
"@vue/compiler-dom" "3.5.16"
"@vue/compiler-ssr" "3.5.16"
"@vue/shared" "3.5.16"
- "estree-walker" "^2.0.2"
- "magic-string" "^0.30.17"
- "postcss" "^8.5.3"
- "source-map-js" "^1.2.1"
+ estree-walker "^2.0.2"
+ magic-string "^0.30.17"
+ postcss "^8.5.3"
+ source-map-js "^1.2.1"
"@vue/compiler-ssr@3.5.16":
- "integrity" "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A=="
- "resolved" "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz"
+ integrity sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==
dependencies:
"@vue/compiler-dom" "3.5.16"
"@vue/shared" "3.5.16"
"@vue/devtools-api@^7.7.0":
- "integrity" "sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw=="
- "resolved" "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.6.tgz"
- "version" "7.7.6"
+ version "7.7.6"
+ resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.6.tgz"
+ integrity sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==
dependencies:
"@vue/devtools-kit" "^7.7.6"
"@vue/devtools-kit@^7.7.6":
- "integrity" "sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA=="
- "resolved" "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz"
- "version" "7.7.6"
+ version "7.7.6"
+ resolved "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz"
+ integrity sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==
dependencies:
"@vue/devtools-shared" "^7.7.6"
- "birpc" "^2.3.0"
- "hookable" "^5.5.3"
- "mitt" "^3.0.1"
- "perfect-debounce" "^1.0.0"
- "speakingurl" "^14.0.1"
- "superjson" "^2.2.2"
+ birpc "^2.3.0"
+ hookable "^5.5.3"
+ mitt "^3.0.1"
+ perfect-debounce "^1.0.0"
+ speakingurl "^14.0.1"
+ superjson "^2.2.2"
"@vue/devtools-shared@^7.7.6":
- "integrity" "sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA=="
- "resolved" "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz"
- "version" "7.7.6"
+ version "7.7.6"
+ resolved "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz"
+ integrity sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==
dependencies:
- "rfdc" "^1.4.1"
+ rfdc "^1.4.1"
"@vue/reactivity@3.5.16":
- "integrity" "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA=="
- "resolved" "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz"
+ integrity sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==
dependencies:
"@vue/shared" "3.5.16"
"@vue/runtime-core@3.5.16":
- "integrity" "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ=="
- "resolved" "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz"
+ integrity sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==
dependencies:
"@vue/reactivity" "3.5.16"
"@vue/shared" "3.5.16"
"@vue/runtime-dom@3.5.16":
- "integrity" "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww=="
- "resolved" "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz"
+ integrity sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==
dependencies:
"@vue/reactivity" "3.5.16"
"@vue/runtime-core" "3.5.16"
"@vue/shared" "3.5.16"
- "csstype" "^3.1.3"
+ csstype "^3.1.3"
"@vue/server-renderer@3.5.16":
- "integrity" "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg=="
- "resolved" "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz"
- "version" "3.5.16"
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz"
+ integrity sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==
dependencies:
"@vue/compiler-ssr" "3.5.16"
"@vue/shared" "3.5.16"
-"@vue/shared@^3.5.13", "@vue/shared@3.5.16":
- "integrity" "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="
- "resolved" "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz"
- "version" "3.5.16"
+"@vue/shared@3.5.16", "@vue/shared@^3.5.13":
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz"
+ integrity sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==
-"@vueuse/core@^12.4.0", "@vueuse/core@12.8.2":
- "integrity" "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="
- "resolved" "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz"
- "version" "12.8.2"
+"@vueuse/core@12.8.2", "@vueuse/core@^12.4.0":
+ version "12.8.2"
+ resolved "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz"
+ integrity sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==
dependencies:
"@types/web-bluetooth" "^0.0.21"
"@vueuse/metadata" "12.8.2"
"@vueuse/shared" "12.8.2"
- "vue" "^3.5.13"
+ vue "^3.5.13"
"@vueuse/integrations@^12.4.0":
- "integrity" "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g=="
- "resolved" "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz"
- "version" "12.8.2"
+ version "12.8.2"
+ resolved "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz"
+ integrity sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==
dependencies:
"@vueuse/core" "12.8.2"
"@vueuse/shared" "12.8.2"
- "vue" "^3.5.13"
+ vue "^3.5.13"
"@vueuse/metadata@12.8.2":
- "integrity" "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="
- "resolved" "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz"
- "version" "12.8.2"
+ version "12.8.2"
+ resolved "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz"
+ integrity sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==
"@vueuse/shared@12.8.2":
- "integrity" "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="
- "resolved" "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz"
- "version" "12.8.2"
+ version "12.8.2"
+ resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz"
+ integrity sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==
dependencies:
- "vue" "^3.5.13"
+ vue "^3.5.13"
-"acorn@^8.14.0":
- "integrity" "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="
- "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz"
- "version" "8.14.1"
+acorn@^8.14.0:
+ version "8.14.1"
+ resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz"
+ integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
-"algoliasearch@^5.14.2", "algoliasearch@>= 4.9.1 < 6":
- "integrity" "sha512-2PvAgvxxJzA3+dB+ERfS2JPdvUsxNf89Cc2GF5iCcFupTULOwmbfinvqrC4Qj9nHJJDNf494NqEN/1f9177ZTQ=="
- "resolved" "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.27.0.tgz"
- "version" "5.27.0"
+algoliasearch@^5.14.2:
+ version "5.27.0"
+ resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.27.0.tgz"
+ integrity sha512-2PvAgvxxJzA3+dB+ERfS2JPdvUsxNf89Cc2GF5iCcFupTULOwmbfinvqrC4Qj9nHJJDNf494NqEN/1f9177ZTQ==
dependencies:
"@algolia/client-abtesting" "5.27.0"
"@algolia/client-analytics" "5.27.0"
@@ -544,86 +749,86 @@
"@algolia/requester-fetch" "5.27.0"
"@algolia/requester-node-http" "5.27.0"
-"birpc@^2.3.0":
- "integrity" "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g=="
- "resolved" "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz"
- "version" "2.3.0"
-
-"ccount@^2.0.0":
- "integrity" "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
- "resolved" "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz"
- "version" "2.0.1"
-
-"character-entities-html4@^2.0.0":
- "integrity" "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="
- "resolved" "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz"
- "version" "2.1.0"
-
-"character-entities-legacy@^3.0.0":
- "integrity" "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="
- "resolved" "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz"
- "version" "3.0.0"
-
-"comma-separated-tokens@^2.0.0":
- "integrity" "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="
- "resolved" "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
- "version" "2.0.3"
-
-"confbox@^0.1.8":
- "integrity" "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="
- "resolved" "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz"
- "version" "0.1.8"
-
-"confbox@^0.2.1":
- "integrity" "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="
- "resolved" "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz"
- "version" "0.2.2"
-
-"copy-anything@^3.0.2":
- "integrity" "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="
- "resolved" "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz"
- "version" "3.0.5"
- dependencies:
- "is-what" "^4.1.8"
-
-"csstype@^3.1.3":
- "integrity" "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
- "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
- "version" "3.1.3"
-
-"debug@^4.4.0":
- "integrity" "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="
- "resolved" "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
- "version" "4.4.0"
- dependencies:
- "ms" "^2.1.3"
-
-"dequal@^2.0.0":
- "integrity" "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
- "resolved" "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
- "version" "2.0.3"
-
-"devlop@^1.0.0":
- "integrity" "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="
- "resolved" "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz"
- "version" "1.1.0"
- dependencies:
- "dequal" "^2.0.0"
-
-"emoji-regex-xs@^1.0.0":
- "integrity" "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="
- "resolved" "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz"
- "version" "1.0.0"
-
-"entities@^4.5.0":
- "integrity" "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
- "resolved" "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
- "version" "4.5.0"
-
-"esbuild@^0.21.3":
- "integrity" "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="
- "resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz"
- "version" "0.21.5"
+birpc@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz"
+ integrity sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==
+
+ccount@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz"
+ integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
+
+character-entities-html4@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz"
+ integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==
+
+character-entities-legacy@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz"
+ integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==
+
+comma-separated-tokens@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
+ integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
+
+confbox@^0.1.8:
+ version "0.1.8"
+ resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz"
+ integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==
+
+confbox@^0.2.1:
+ version "0.2.2"
+ resolved "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz"
+ integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==
+
+copy-anything@^3.0.2:
+ version "3.0.5"
+ resolved "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz"
+ integrity sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==
+ dependencies:
+ is-what "^4.1.8"
+
+csstype@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
+ integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+
+debug@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
+ integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+ dependencies:
+ ms "^2.1.3"
+
+dequal@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
+ integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
+
+devlop@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz"
+ integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==
+ dependencies:
+ dequal "^2.0.0"
+
+emoji-regex-xs@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz"
+ integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==
+
+entities@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
+ integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
+esbuild@^0.21.3:
+ version "0.21.5"
+ resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz"
+ integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
optionalDependencies:
"@esbuild/aix-ppc64" "0.21.5"
"@esbuild/android-arm" "0.21.5"
@@ -649,274 +854,274 @@
"@esbuild/win32-ia32" "0.21.5"
"@esbuild/win32-x64" "0.21.5"
-"estree-walker@^2.0.2":
- "integrity" "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
- "resolved" "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
- "version" "2.0.2"
+estree-walker@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
+ integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
-"exsolve@^1.0.1":
- "integrity" "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="
- "resolved" "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz"
- "version" "1.0.5"
+exsolve@^1.0.1:
+ version "1.0.5"
+ resolved "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz"
+ integrity sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==
-"focus-trap@^7", "focus-trap@^7.6.4":
- "integrity" "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg=="
- "resolved" "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz"
- "version" "7.6.5"
+focus-trap@^7.6.4:
+ version "7.6.5"
+ resolved "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz"
+ integrity sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==
dependencies:
- "tabbable" "^6.2.0"
+ tabbable "^6.2.0"
-"fsevents@~2.3.2", "fsevents@~2.3.3":
- "integrity" "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
- "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
- "version" "2.3.3"
+fsevents@~2.3.2, fsevents@~2.3.3:
+ version "2.3.3"
+ resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
-"globals@^15.14.0":
- "integrity" "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="
- "resolved" "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz"
- "version" "15.15.0"
+globals@^15.14.0:
+ version "15.15.0"
+ resolved "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz"
+ integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==
-"hast-util-to-html@^9.0.4":
- "integrity" "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="
- "resolved" "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz"
- "version" "9.0.5"
+hast-util-to-html@^9.0.4:
+ version "9.0.5"
+ resolved "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz"
+ integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==
dependencies:
"@types/hast" "^3.0.0"
"@types/unist" "^3.0.0"
- "ccount" "^2.0.0"
- "comma-separated-tokens" "^2.0.0"
- "hast-util-whitespace" "^3.0.0"
- "html-void-elements" "^3.0.0"
- "mdast-util-to-hast" "^13.0.0"
- "property-information" "^7.0.0"
- "space-separated-tokens" "^2.0.0"
- "stringify-entities" "^4.0.0"
- "zwitch" "^2.0.4"
-
-"hast-util-whitespace@^3.0.0":
- "integrity" "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="
- "resolved" "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz"
- "version" "3.0.0"
+ ccount "^2.0.0"
+ comma-separated-tokens "^2.0.0"
+ hast-util-whitespace "^3.0.0"
+ html-void-elements "^3.0.0"
+ mdast-util-to-hast "^13.0.0"
+ property-information "^7.0.0"
+ space-separated-tokens "^2.0.0"
+ stringify-entities "^4.0.0"
+ zwitch "^2.0.4"
+
+hast-util-whitespace@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz"
+ integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==
dependencies:
"@types/hast" "^3.0.0"
-"hookable@^5.5.3":
- "integrity" "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
- "resolved" "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz"
- "version" "5.5.3"
+hookable@^5.5.3:
+ version "5.5.3"
+ resolved "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz"
+ integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==
-"html-void-elements@^3.0.0":
- "integrity" "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="
- "resolved" "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz"
- "version" "3.0.0"
+html-void-elements@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz"
+ integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
-"is-what@^4.1.8":
- "integrity" "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="
- "resolved" "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz"
- "version" "4.1.16"
+is-what@^4.1.8:
+ version "4.1.16"
+ resolved "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz"
+ integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==
-"kolorist@^1.8.0":
- "integrity" "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="
- "resolved" "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz"
- "version" "1.8.0"
+kolorist@^1.8.0:
+ version "1.8.0"
+ resolved "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz"
+ integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
-"local-pkg@^1.0.0":
- "integrity" "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg=="
- "resolved" "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz"
- "version" "1.1.1"
+local-pkg@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz"
+ integrity sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==
dependencies:
- "mlly" "^1.7.4"
- "pkg-types" "^2.0.1"
- "quansync" "^0.2.8"
+ mlly "^1.7.4"
+ pkg-types "^2.0.1"
+ quansync "^0.2.8"
-"magic-string@^0.30.17":
- "integrity" "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="
- "resolved" "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz"
- "version" "0.30.17"
+magic-string@^0.30.17:
+ version "0.30.17"
+ resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz"
+ integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
-"mark.js@8.11.1":
- "integrity" "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="
- "resolved" "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz"
- "version" "8.11.1"
+mark.js@8.11.1:
+ version "8.11.1"
+ resolved "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz"
+ integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==
-"mdast-util-to-hast@^13.0.0":
- "integrity" "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="
- "resolved" "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz"
- "version" "13.2.0"
+mdast-util-to-hast@^13.0.0:
+ version "13.2.0"
+ resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz"
+ integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==
dependencies:
"@types/hast" "^3.0.0"
"@types/mdast" "^4.0.0"
"@ungap/structured-clone" "^1.0.0"
- "devlop" "^1.0.0"
- "micromark-util-sanitize-uri" "^2.0.0"
- "trim-lines" "^3.0.0"
- "unist-util-position" "^5.0.0"
- "unist-util-visit" "^5.0.0"
- "vfile" "^6.0.0"
-
-"micromark-util-character@^2.0.0":
- "integrity" "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="
- "resolved" "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz"
- "version" "2.1.1"
- dependencies:
- "micromark-util-symbol" "^2.0.0"
- "micromark-util-types" "^2.0.0"
-
-"micromark-util-encode@^2.0.0":
- "integrity" "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="
- "resolved" "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz"
- "version" "2.0.1"
-
-"micromark-util-sanitize-uri@^2.0.0":
- "integrity" "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="
- "resolved" "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz"
- "version" "2.0.1"
- dependencies:
- "micromark-util-character" "^2.0.0"
- "micromark-util-encode" "^2.0.0"
- "micromark-util-symbol" "^2.0.0"
-
-"micromark-util-symbol@^2.0.0":
- "integrity" "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="
- "resolved" "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz"
- "version" "2.0.1"
-
-"micromark-util-types@^2.0.0":
- "integrity" "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="
- "resolved" "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz"
- "version" "2.0.2"
-
-"minisearch@^7.1.1":
- "integrity" "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA=="
- "resolved" "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz"
- "version" "7.1.2"
-
-"mitt@^3.0.1":
- "integrity" "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
- "resolved" "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz"
- "version" "3.0.1"
-
-"mlly@^1.7.4":
- "integrity" "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw=="
- "resolved" "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz"
- "version" "1.7.4"
- dependencies:
- "acorn" "^8.14.0"
- "pathe" "^2.0.1"
- "pkg-types" "^1.3.0"
- "ufo" "^1.5.4"
-
-"ms@^2.1.3":
- "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
- "version" "2.1.3"
-
-"nanoid@^3.3.11":
- "integrity" "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="
- "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz"
- "version" "3.3.11"
-
-"oniguruma-to-es@^3.1.0":
- "integrity" "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="
- "resolved" "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz"
- "version" "3.1.1"
- dependencies:
- "emoji-regex-xs" "^1.0.0"
- "regex" "^6.0.1"
- "regex-recursion" "^6.0.2"
-
-"package-manager-detector@^1.3.0":
- "integrity" "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="
- "resolved" "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz"
- "version" "1.3.0"
-
-"pathe@^2.0.1", "pathe@^2.0.3":
- "integrity" "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="
- "resolved" "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz"
- "version" "2.0.3"
-
-"perfect-debounce@^1.0.0":
- "integrity" "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
- "resolved" "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz"
- "version" "1.0.0"
-
-"picocolors@^1.1.1":
- "integrity" "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
- "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
- "version" "1.1.1"
-
-"pkg-types@^1.3.0":
- "integrity" "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="
- "resolved" "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz"
- "version" "1.3.1"
- dependencies:
- "confbox" "^0.1.8"
- "mlly" "^1.7.4"
- "pathe" "^2.0.1"
-
-"pkg-types@^2.0.1":
- "integrity" "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="
- "resolved" "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz"
- "version" "2.1.0"
- dependencies:
- "confbox" "^0.2.1"
- "exsolve" "^1.0.1"
- "pathe" "^2.0.3"
-
-"postcss@^8", "postcss@^8.4.43", "postcss@^8.5.3":
- "integrity" "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="
- "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz"
- "version" "8.5.4"
- dependencies:
- "nanoid" "^3.3.11"
- "picocolors" "^1.1.1"
- "source-map-js" "^1.2.1"
-
-"preact@^10.0.0":
- "integrity" "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ=="
- "resolved" "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz"
- "version" "10.26.8"
-
-"property-information@^7.0.0":
- "integrity" "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="
- "resolved" "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz"
- "version" "7.1.0"
-
-"quansync@^0.2.8":
- "integrity" "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="
- "resolved" "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz"
- "version" "0.2.10"
-
-"regex-recursion@^6.0.2":
- "integrity" "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="
- "resolved" "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz"
- "version" "6.0.2"
- dependencies:
- "regex-utilities" "^2.3.0"
-
-"regex-utilities@^2.3.0":
- "integrity" "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="
- "resolved" "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz"
- "version" "2.3.0"
-
-"regex@^6.0.1":
- "integrity" "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="
- "resolved" "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz"
- "version" "6.0.1"
- dependencies:
- "regex-utilities" "^2.3.0"
-
-"rfdc@^1.4.1":
- "integrity" "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
- "resolved" "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz"
- "version" "1.4.1"
-
-"rollup@^4.20.0":
- "integrity" "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw=="
- "resolved" "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz"
- "version" "4.42.0"
+ devlop "^1.0.0"
+ micromark-util-sanitize-uri "^2.0.0"
+ trim-lines "^3.0.0"
+ unist-util-position "^5.0.0"
+ unist-util-visit "^5.0.0"
+ vfile "^6.0.0"
+
+micromark-util-character@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz"
+ integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==
+ dependencies:
+ micromark-util-symbol "^2.0.0"
+ micromark-util-types "^2.0.0"
+
+micromark-util-encode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz"
+ integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==
+
+micromark-util-sanitize-uri@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz"
+ integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==
+ dependencies:
+ micromark-util-character "^2.0.0"
+ micromark-util-encode "^2.0.0"
+ micromark-util-symbol "^2.0.0"
+
+micromark-util-symbol@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz"
+ integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==
+
+micromark-util-types@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz"
+ integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==
+
+minisearch@^7.1.1:
+ version "7.1.2"
+ resolved "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz"
+ integrity sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==
+
+mitt@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz"
+ integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
+
+mlly@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz"
+ integrity sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==
+ dependencies:
+ acorn "^8.14.0"
+ pathe "^2.0.1"
+ pkg-types "^1.3.0"
+ ufo "^1.5.4"
+
+ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+nanoid@^3.3.11:
+ version "3.3.11"
+ resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz"
+ integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
+
+oniguruma-to-es@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz"
+ integrity sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==
+ dependencies:
+ emoji-regex-xs "^1.0.0"
+ regex "^6.0.1"
+ regex-recursion "^6.0.2"
+
+package-manager-detector@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz"
+ integrity sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==
+
+pathe@^2.0.1, pathe@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz"
+ integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
+
+perfect-debounce@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz"
+ integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==
+
+picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+pkg-types@^1.3.0:
+ version "1.3.1"
+ resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz"
+ integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==
+ dependencies:
+ confbox "^0.1.8"
+ mlly "^1.7.4"
+ pathe "^2.0.1"
+
+pkg-types@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz"
+ integrity sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==
+ dependencies:
+ confbox "^0.2.1"
+ exsolve "^1.0.1"
+ pathe "^2.0.3"
+
+postcss@^8.4.43, postcss@^8.5.3:
+ version "8.5.4"
+ resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz"
+ integrity sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==
+ dependencies:
+ nanoid "^3.3.11"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
+
+preact@^10.0.0:
+ version "10.26.8"
+ resolved "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz"
+ integrity sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==
+
+property-information@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz"
+ integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==
+
+quansync@^0.2.8:
+ version "0.2.10"
+ resolved "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz"
+ integrity sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==
+
+regex-recursion@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz"
+ integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==
+ dependencies:
+ regex-utilities "^2.3.0"
+
+regex-utilities@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz"
+ integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==
+
+regex@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz"
+ integrity sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==
+ dependencies:
+ regex-utilities "^2.3.0"
+
+rfdc@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz"
+ integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==
+
+rollup@^4.20.0:
+ version "4.42.0"
+ resolved "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz"
+ integrity sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==
dependencies:
"@types/estree" "1.0.7"
optionalDependencies:
@@ -940,17 +1145,12 @@
"@rollup/rollup-win32-arm64-msvc" "4.42.0"
"@rollup/rollup-win32-ia32-msvc" "4.42.0"
"@rollup/rollup-win32-x64-msvc" "4.42.0"
- "fsevents" "~2.3.2"
+ fsevents "~2.3.2"
-"search-insights@>= 1 < 3":
- "integrity" "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="
- "resolved" "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz"
- "version" "2.17.3"
-
-"shiki@^2.1.0":
- "integrity" "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="
- "resolved" "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz"
- "version" "2.5.0"
+shiki@^2.1.0:
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz"
+ integrity sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==
dependencies:
"@shikijs/core" "2.5.0"
"@shikijs/engine-javascript" "2.5.0"
@@ -961,134 +1161,139 @@
"@shikijs/vscode-textmate" "^10.0.2"
"@types/hast" "^3.0.4"
-"source-map-js@^1.2.1":
- "integrity" "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
- "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
- "version" "1.2.1"
-
-"space-separated-tokens@^2.0.0":
- "integrity" "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="
- "resolved" "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz"
- "version" "2.0.2"
-
-"speakingurl@^14.0.1":
- "integrity" "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="
- "resolved" "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz"
- "version" "14.0.1"
-
-"stringify-entities@^4.0.0":
- "integrity" "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="
- "resolved" "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
- "version" "4.0.4"
- dependencies:
- "character-entities-html4" "^2.0.0"
- "character-entities-legacy" "^3.0.0"
-
-"superjson@^2.2.2":
- "integrity" "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q=="
- "resolved" "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz"
- "version" "2.2.2"
- dependencies:
- "copy-anything" "^3.0.2"
-
-"tabbable@^6.2.0":
- "integrity" "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
- "resolved" "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz"
- "version" "6.2.0"
-
-"tinyexec@^1.0.1":
- "integrity" "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="
- "resolved" "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz"
- "version" "1.0.1"
-
-"trim-lines@^3.0.0":
- "integrity" "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="
- "resolved" "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz"
- "version" "3.0.1"
-
-"ufo@^1.5.4":
- "integrity" "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="
- "resolved" "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz"
- "version" "1.6.1"
-
-"unist-util-is@^6.0.0":
- "integrity" "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="
- "resolved" "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz"
- "version" "6.0.0"
+source-map-js@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
+ integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+space-separated-tokens@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz"
+ integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
+
+speakingurl@^14.0.1:
+ version "14.0.1"
+ resolved "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz"
+ integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==
+
+stringify-entities@^4.0.0:
+ version "4.0.4"
+ resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
+ integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==
+ dependencies:
+ character-entities-html4 "^2.0.0"
+ character-entities-legacy "^3.0.0"
+
+superjson@^2.2.2:
+ version "2.2.2"
+ resolved "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz"
+ integrity sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==
+ dependencies:
+ copy-anything "^3.0.2"
+
+tabbable@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz"
+ integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
+
+tinyexec@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz"
+ integrity sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==
+
+trim-lines@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz"
+ integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==
+
+ufo@^1.5.4:
+ version "1.6.1"
+ resolved "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz"
+ integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==
+
+unist-util-is@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz"
+ integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==
dependencies:
"@types/unist" "^3.0.0"
-"unist-util-position@^5.0.0":
- "integrity" "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="
- "resolved" "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz"
- "version" "5.0.0"
+unist-util-position@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz"
+ integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==
dependencies:
"@types/unist" "^3.0.0"
-"unist-util-stringify-position@^4.0.0":
- "integrity" "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="
- "resolved" "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz"
- "version" "4.0.0"
+unist-util-stringify-position@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz"
+ integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==
dependencies:
"@types/unist" "^3.0.0"
-"unist-util-visit-parents@^6.0.0":
- "integrity" "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="
- "resolved" "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz"
- "version" "6.0.1"
+unist-util-visit-parents@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz"
+ integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==
dependencies:
"@types/unist" "^3.0.0"
- "unist-util-is" "^6.0.0"
+ unist-util-is "^6.0.0"
-"unist-util-visit@^5.0.0":
- "integrity" "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="
- "resolved" "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz"
- "version" "5.0.0"
+unist-util-visit@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz"
+ integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==
dependencies:
"@types/unist" "^3.0.0"
- "unist-util-is" "^6.0.0"
- "unist-util-visit-parents" "^6.0.0"
+ unist-util-is "^6.0.0"
+ unist-util-visit-parents "^6.0.0"
-"vfile-message@^4.0.0":
- "integrity" "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="
- "resolved" "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz"
- "version" "4.0.2"
+vfile-message@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz"
+ integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==
dependencies:
"@types/unist" "^3.0.0"
- "unist-util-stringify-position" "^4.0.0"
+ unist-util-stringify-position "^4.0.0"
-"vfile@^6.0.0":
- "integrity" "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="
- "resolved" "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz"
- "version" "6.0.3"
+vfile@^6.0.0:
+ version "6.0.3"
+ resolved "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz"
+ integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==
dependencies:
"@types/unist" "^3.0.0"
- "vfile-message" "^4.0.0"
+ vfile-message "^4.0.0"
-"vite@^5.0.0 || ^6.0.0", "vite@^5.4.14":
- "integrity" "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="
- "resolved" "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz"
- "version" "5.4.19"
+vite@^5.4.14:
+ version "5.4.19"
+ resolved "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz"
+ integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==
dependencies:
- "esbuild" "^0.21.3"
- "postcss" "^8.4.43"
- "rollup" "^4.20.0"
+ esbuild "^0.21.3"
+ postcss "^8.4.43"
+ rollup "^4.20.0"
optionalDependencies:
- "fsevents" "~2.3.3"
+ fsevents "~2.3.3"
-"vitepress-plugin-group-icons@^1.5.2":
- "integrity" "sha512-zen07KxZ83y3eecou4EraaEgwIriwHaB5Q0cHAmS4yO1UZEQvbljTylHPqiJ7LNkV39U8VehfcyquAJXg/26LA=="
- "resolved" "https://registry.npmjs.org/vitepress-plugin-group-icons/-/vitepress-plugin-group-icons-1.5.2.tgz"
- "version" "1.5.2"
+vitepress-plugin-group-icons@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.npmjs.org/vitepress-plugin-group-icons/-/vitepress-plugin-group-icons-1.5.2.tgz"
+ integrity sha512-zen07KxZ83y3eecou4EraaEgwIriwHaB5Q0cHAmS4yO1UZEQvbljTylHPqiJ7LNkV39U8VehfcyquAJXg/26LA==
dependencies:
"@iconify-json/logos" "^1.2.4"
"@iconify-json/vscode-icons" "^1.2.18"
"@iconify/utils" "^2.3.0"
-"vitepress@^1.6.3":
- "integrity" "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw=="
- "resolved" "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz"
- "version" "1.6.3"
+vitepress-plugin-tabs@^0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/vitepress-plugin-tabs/-/vitepress-plugin-tabs-0.7.1.tgz#73e0537fee5bac1fa00c941165241eb941b13a80"
+ integrity sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ==
+
+vitepress@^1.6.3:
+ version "1.6.3"
+ resolved "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz"
+ integrity sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==
dependencies:
"@docsearch/css" "3.8.2"
"@docsearch/js" "3.8.2"
@@ -1102,17 +1307,17 @@
"@vue/shared" "^3.5.13"
"@vueuse/core" "^12.4.0"
"@vueuse/integrations" "^12.4.0"
- "focus-trap" "^7.6.4"
- "mark.js" "8.11.1"
- "minisearch" "^7.1.1"
- "shiki" "^2.1.0"
- "vite" "^5.4.14"
- "vue" "^3.5.13"
-
-"vue@^3.2.25", "vue@^3.5.13", "vue@3.5.16":
- "integrity" "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w=="
- "resolved" "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz"
- "version" "3.5.16"
+ focus-trap "^7.6.4"
+ mark.js "8.11.1"
+ minisearch "^7.1.1"
+ shiki "^2.1.0"
+ vite "^5.4.14"
+ vue "^3.5.13"
+
+vue@^3.5.13:
+ version "3.5.16"
+ resolved "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz"
+ integrity sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==
dependencies:
"@vue/compiler-dom" "3.5.16"
"@vue/compiler-sfc" "3.5.16"
@@ -1120,7 +1325,7 @@
"@vue/server-renderer" "3.5.16"
"@vue/shared" "3.5.16"
-"zwitch@^2.0.4":
- "integrity" "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
- "resolved" "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"
- "version" "2.0.4"
+zwitch@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"
+ integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==