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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/elixir-sdk/lib/firecrawl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ defmodule Firecrawl do
@start_agent_schema NimbleOptions.new!([
audit_metadata: [type: :keyword_list, keys: [username: [type: :string, required: true]], doc: "User attribution to include with SIEM logging events."],
max_credits: [type: {:or, [:integer, :float]}, doc: "Maximum credits to spend on this agent task. Defaults to 2500 if not set. Values above 2,500 are always billed as paid requests."],
model: [type: {:or, [{:in, [:"spark-1-mini", :"spark-1-pro"]}, :string]}, doc: "The model to use for the agent task. spark-1-mini (default) is 60% cheaper, spark-1-pro offers higher accuracy for complex tasks"],
model: [type: {:or, [{:in, [:"spark-1-mini", :"spark-1-pro", :"spark-2"]}, :string]}, doc: "The model to use for the agent task. spark-2 (default) handles most tasks, spark-1-mini is 60% cheaper than spark-1-pro, spark-1-pro offers higher accuracy for complex tasks"],
prompt: [type: :string, required: true, doc: "The prompt describing what data to extract"],
schema: [type: :any, doc: "Optional JSON schema to structure the extracted data"],
strict_constrain_to_urls: [type: :boolean, doc: "If true, agent will only visit URLs provided in the urls array"],
Expand Down
2 changes: 1 addition & 1 deletion apps/elixir-sdk/mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Firecrawl.MixProject do
use Mix.Project

@version "1.9.1"
@version "1.9.2"
@source_url "https://github.com/firecrawl/firecrawl/tree/main/apps/elixir-sdk"

def project do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private Builder() {}
public Builder maxCredits(Integer maxCredits) { this.maxCredits = maxCredits; return this; }
/** Don't navigate outside provided URLs. */
public Builder strictConstrainToURLs(Boolean strictConstrainToURLs) { this.strictConstrainToURLs = strictConstrainToURLs; return this; }
/** Agent model: "spark-1-pro" or "spark-1-mini". */
/** Agent model: "spark-2" (default), "spark-1-pro", or "spark-1-mini". */
public Builder model(String model) { this.model = model; return this; }
/** Webhook configuration. */
public Builder webhook(WebhookConfig webhook) { this.webhook = webhook; return this; }
Expand Down
2 changes: 1 addition & 1 deletion apps/js-sdk/firecrawl/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mendable/firecrawl-js",
"version": "4.34.0",
"version": "4.34.1",
"description": "JavaScript SDK for the Firecrawl API: web scraping, crawling, web search, and scientific literature search over a research paper index of PubMed, bioRxiv, medRxiv and arXiv abstracts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion apps/js-sdk/firecrawl/src/v2/methods/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function prepareAgentPayload(args: {
origin?: string;
maxCredits?: number;
strictConstrainToURLs?: boolean;
model?: "spark-1-pro" | "spark-1-mini";
model?: "spark-1-pro" | "spark-1-mini" | "spark-2";
webhook?: string | AgentWebhookConfig;
threatProtection?: ThreatProtectionOptions;
auditMetadata?: AuditMetadata;
Expand Down
7 changes: 6 additions & 1 deletion apps/js-sdk/firecrawl/src/v2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1244,7 +1244,12 @@ export interface AgentStatusResponse {
status: "processing" | "completed" | "failed";
error?: string;
data?: unknown;
model?: "spark-1-pro" | "spark-1-mini";
/**
* Server-provided model name. Widened past the request-side union on
* purpose: new models ship without an SDK release, so pinning this to known
* names makes every future model a type error at the call site.
*/
model?: "spark-1-pro" | "spark-1-mini" | "spark-2" | (string & {});
expiresAt: string;
creditsUsed?: number;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/python-sdk/firecrawl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
V1ChangeTrackingOptions,
)

__version__ = "4.37.0"
__version__ = "4.37.1"

# Define the logger for the Firecrawl project
logger: logging.Logger = logging.getLogger("firecrawl")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import List, Optional

from firecrawl.v2.methods.agent import _prepare_agent_request
from firecrawl.v2.types import AgentWebhookConfig
from firecrawl.v2.types import AgentResponse, AgentWebhookConfig


class TestAgentRequestPreparation:
Expand Down Expand Up @@ -337,3 +337,34 @@ def test_request_all_fields_with_webhook(self):
assert data["model"] == "spark-1-pro"
assert data["webhook"]["url"] == "https://example.com/webhook"

@pytest.mark.parametrize("model", ["spark-2", "spark-1-pro", "spark-1-mini"])
def test_model_forwarded_verbatim(self, model):
"""Every documented model, including spark-2, reaches the request body."""
data = _prepare_agent_request(None, prompt="Model test", model=model)

assert data["model"] == model


class TestAgentResponseParsing:
"""Unit tests for parsing agent status payloads."""

@pytest.mark.parametrize("model", ["spark-2", "spark-1-pro", "spark-1-mini"])
def test_status_payload_parses_known_models(self, model):
"""A status payload naming any current model parses.

/v2/agent/:id always returns `model`, and it defaults to spark-2, so a
narrow type here breaks every poll of a default-model job.
"""
response = AgentResponse(
**{"success": True, "id": "job-1", "status": "completed", "model": model}
)

assert response.model == model

def test_status_payload_parses_unreleased_model(self):
"""An unknown model name must not raise; the server ships models first."""
response = AgentResponse(
**{"success": True, "id": "job-1", "status": "completed", "model": "spark-3"}
)

assert response.model == "spark-3"
8 changes: 4 additions & 4 deletions apps/python-sdk/firecrawl/v2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1357,7 +1357,7 @@ def start_agent(
integration: Optional[str] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand All @@ -1370,7 +1370,7 @@ def start_agent(
schema: Target JSON schema for the output (dict or Pydantic BaseModel)
integration: Integration tag/name
max_credits: Maximum credits to use (optional)
model: Model to use for the agent ("spark-1-pro" or "spark-1-mini")
model: Model to use for the agent ("spark-2" (default), "spark-1-pro", or "spark-1-mini")
webhook: Webhook URL or configuration for notifications
threat_protection: Enterprise per-request override of the team's
threat protection policy
Expand Down Expand Up @@ -1403,7 +1403,7 @@ def agent(
timeout: Optional[int] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand All @@ -1418,7 +1418,7 @@ def agent(
poll_interval: Seconds between status checks
timeout: Maximum seconds to wait (None for no timeout)
max_credits: Maximum credits to use (optional)
model: Model to use for the agent ("spark-1-pro" or "spark-1-mini")
model: Model to use for the agent ("spark-2" (default), "spark-1-pro", or "spark-1-mini")
webhook: Webhook URL or configuration for notifications
threat_protection: Enterprise per-request override of the team's
threat protection policy
Expand Down
4 changes: 2 additions & 2 deletions apps/python-sdk/firecrawl/v2/client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ async def agent(
timeout: Optional[int] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down Expand Up @@ -727,7 +727,7 @@ async def start_agent(
integration: Optional[str] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down
6 changes: 3 additions & 3 deletions apps/python-sdk/firecrawl/v2/methods/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def _prepare_agent_request(
integration: Optional[str] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down Expand Up @@ -73,7 +73,7 @@ def start_agent(
integration: Optional[str] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down Expand Up @@ -133,7 +133,7 @@ def agent(
timeout: Optional[int] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down
6 changes: 3 additions & 3 deletions apps/python-sdk/firecrawl/v2/methods/aio/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def _prepare_agent_request(
integration: Optional[str] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down Expand Up @@ -73,7 +73,7 @@ async def start_agent(
integration: Optional[str] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down Expand Up @@ -133,7 +133,7 @@ async def agent(
timeout: Optional[int] = None,
max_credits: Optional[int] = None,
strict_constrain_to_urls: Optional[bool] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None,
model: Optional[Literal["spark-1-pro", "spark-1-mini", "spark-2"]] = None,
webhook: Optional[Union[str, AgentWebhookConfig]] = None,
threat_protection: Optional[ThreatProtectionOptions] = None,
audit_metadata: Optional[AuditMetadata] = None,
Expand Down
5 changes: 4 additions & 1 deletion apps/python-sdk/firecrawl/v2/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,10 @@ class AgentResponse(BaseModel):
status: Optional[Literal["processing", "completed", "failed"]] = None
data: Optional[Any] = None
error: Optional[str] = None
model: Optional[Literal["spark-1-pro", "spark-1-mini"]] = None
# Deliberately a plain str, not a Literal: this is server-provided and new
# models ship without an SDK release, so a narrow type turns an unknown
# model name into a ValidationError on every status poll.
model: Optional[str] = None
expires_at: Optional[datetime] = None
credits_used: Optional[int] = None

Expand Down
12 changes: 12 additions & 0 deletions apps/rust-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
## CHANGELOG

## [2.15.0] - 2026-08-21

### Added

- Added `AgentModel::Spark2` for the `spark-2` agent model, now the server-side
default. Agent status responses for jobs running it previously failed to
deserialize.
- Added `AgentModel::Unknown`, a `#[serde(other)]` catch-all so an agent status
response naming a model this release predates degrades to `Unknown` instead
of failing the parse and breaking the status wait loop. Read-only: it
serializes to `"unknown"`, which the API rejects.

## [2.14.0] - 2026-08-19

### Added
Expand Down
2 changes: 1 addition & 1 deletion apps/rust-sdk/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/rust-sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "firecrawl"
version = "2.14.0"
version = "2.15.0"
edition = "2021"
license = "MIT"
homepage = "https://www.firecrawl.dev/"
Expand Down
2 changes: 1 addition & 1 deletion apps/rust-sdk/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub struct AgentOptions {
/// Strictly constrain the agent to the provided URLs.
pub strict_constrain_to_urls: Option<bool>,

/// Agent model to use.
/// Agent model to use. Defaults to `spark-2` server-side when unset.
pub model: Option<AgentModel>,

/// Webhook configuration for agent notifications.
Expand Down
37 changes: 37 additions & 0 deletions apps/rust-sdk/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,17 @@ pub enum AgentModel {
Spark1Pro,
#[serde(rename = "spark-1-mini")]
Spark1Mini,
#[serde(rename = "spark-2")]
Spark2,
/// A model this SDK release does not know about.
///
/// Read-only catch-all: the server ships models without an SDK release, so
/// an exhaustive enum would fail the whole `AgentStatusResponse` parse — and
/// therefore the status wait loop — the first time an unrecognized name came
/// back. Do not send this variant in a request; it serializes to `"unknown"`,
/// which the API rejects.
#[serde(other)]
Unknown,
}

/// Search source types.
Expand Down Expand Up @@ -1076,6 +1087,32 @@ mod tests {
use super::*;
use serde_json::json;

#[test]
fn test_agent_model_round_trips_every_known_model() {
for (name, expected) in [
("spark-2", AgentModel::Spark2),
("spark-1-pro", AgentModel::Spark1Pro),
("spark-1-mini", AgentModel::Spark1Mini),
] {
let parsed: AgentModel = serde_json::from_str(&format!("\"{name}\""))
.unwrap_or_else(|e| panic!("{name} should deserialize: {e}"));
assert_eq!(parsed, expected);
assert_eq!(
serde_json::to_string(&expected).unwrap(),
format!("\"{name}\"")
);
}
}

#[test]
fn test_agent_model_unknown_degrades_instead_of_failing_the_parse() {
// /v2/agent/:id always returns `model`. A model the server ships before
// this SDK knows about must not take down status polling.
let parsed: AgentModel = serde_json::from_str("\"spark-9-unreleased\"")
.expect("unknown model should deserialize");
assert_eq!(parsed, AgentModel::Unknown);
}

#[test]
fn test_full_document_with_array_metadata() {
let json = json!({
Expand Down
Loading