Skip to content
Closed
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.1] - 2026-04-29

Documentation discoverability fixes — no API changes for end users. The reference docs at `reference.langchain.com/python/langchain-parallel` were rendering pages under the legacy class names (`ChatParallelWeb`, `ParallelWebSearchTool`) and at private module paths (`_types`, `_client`); this release reorganizes the source so the auto-generated reference matches the canonical names used in user-facing docs.

### Changed

- **Canonical class definitions flipped.** `ChatParallel` is now the actual class definition; `ChatParallelWeb` is a back-compat alias (`ChatParallelWeb = ChatParallel`). Same flip for `ParallelSearchTool` / `ParallelWebSearchTool`. Both old and new names continue to import and `isinstance(x, ChatParallelWeb)` continues to work — they're the same class object.
- **`langchain_parallel._types` → `langchain_parallel.types`.** The settings classes (`ExcerptSettings`, `FetchPolicy`, `FullContentSettings`, `SourcePolicy`) now live at the public `types` module path; `_types` remains as a re-export shim so any code importing from the old path keeps working.
- **`langchain_parallel._client`** declares `__all__: list[str] = []` and a "private module" docstring so reference-doc tooling treats it as internal. No public API change — these helpers were never exported from the package root.

### Migration

No code changes required. The two flipped classes and the renamed settings module are fully backward compatible:

- `from langchain_parallel import ChatParallelWeb` and `from langchain_parallel import ChatParallel` both work and resolve to the same class.
- `from langchain_parallel import ParallelWebSearchTool` and `from langchain_parallel import ParallelSearchTool` both work and resolve to the same class.
- `from langchain_parallel._types import ExcerptSettings` continues to work via the back-compat shim; new code should prefer `from langchain_parallel.types import ExcerptSettings` (or the package root: `from langchain_parallel import ExcerptSettings`).

## [0.4.0] - 2026-04-28

This is a feature release covering Phase 2 of the modernization roadmap. Adds five new public surfaces (retriever, Task API, FindAll, Monitor, MCP toolkit) and removes the deprecation paths that 0.3.0 introduced.
Expand Down
12 changes: 6 additions & 6 deletions langchain_parallel/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
from importlib import metadata

from langchain_parallel._types import (
ExcerptSettings,
FetchPolicy,
FullContentSettings,
SourcePolicy,
)
from langchain_parallel.chat_models import ChatParallel, ChatParallelWeb
from langchain_parallel.extract_tool import ParallelExtractTool
from langchain_parallel.findall import (
Expand All @@ -26,6 +20,12 @@
parse_basis,
verify_webhook,
)
from langchain_parallel.types import (
ExcerptSettings,
FetchPolicy,
FullContentSettings,
SourcePolicy,
)

try:
__version__ = metadata.version(__package__ or __name__)
Expand Down
8 changes: 7 additions & 1 deletion langchain_parallel/_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
"""Client utilities for Parallel integration."""
"""Internal client utilities for Parallel integration.

Private module. Helpers in here are not part of the public API and may
change without notice; import from :mod:`langchain_parallel` instead.
"""

from __future__ import annotations

Expand All @@ -8,6 +12,8 @@
import openai
from parallel import AsyncParallel, Parallel

__all__: list[str] = []


def get_api_key(api_key: Optional[str] = None) -> str:
"""Retrieve the Parallel API key from argument or environment variables.
Expand Down
137 changes: 22 additions & 115 deletions langchain_parallel/_types.py
Original file line number Diff line number Diff line change
@@ -1,115 +1,22 @@
"""Common types for Parallel API."""

from __future__ import annotations

import datetime as _dt
from typing import Optional, Union

from pydantic import BaseModel, Field, field_validator, model_validator


class ExcerptSettings(BaseModel):
"""Settings for excerpt extraction."""

max_chars_per_result: Optional[int] = Field(
default=None,
description=(
"Optional upper bound on the total number of characters to include "
"per url. Excerpts may contain fewer characters than this limit to "
"maximize relevance and token efficiency."
),
)


class FullContentSettings(BaseModel):
"""Settings for full content extraction."""

max_chars_per_result: Optional[int] = Field(
default=None,
description=(
"Optional limit on the number of characters to include in the full "
"content for each url. Full content always starts at the beginning "
"of the page and is truncated at the limit if necessary."
),
)


class FetchPolicy(BaseModel):
"""Fetch policy for cache vs live content."""

max_age_seconds: Optional[int] = Field(
default=None,
ge=600,
description=(
"If cached content is older than this, fetch fresh content from the "
"source. Minimum 600 seconds (10 minutes); if not provided, the API "
"uses a dynamic age policy."
),
)
timeout_seconds: Optional[float] = Field(
default=None,
description=(
"Timeout in seconds for fetching live content if unavailable in cache. "
"If unspecified, dynamic timeout will be used (15-60 seconds)."
),
)
disable_cache_fallback: bool = Field(
default=False,
description=(
"If false, fallback to cached content older than max-age if live "
"fetch fails or times out. If true, returns an error instead."
),
)


class SourcePolicy(BaseModel):
"""Domain allow/deny lists and freshness floor for web research.

Apex-domain semantics: include `nature.com`, not `https://www.nature.com`.
Wildcards permitted (e.g. `.org`).
"""

include_domains: Optional[list[str]] = Field(
default=None,
max_length=200,
description=(
"If provided, only sources from these apex domains are returned. "
"Combined include + exclude lists are capped at 200 domains."
),
)
exclude_domains: Optional[list[str]] = Field(
default=None,
max_length=200,
description="If provided, sources from these apex domains are excluded.",
)
after_date: Optional[Union[_dt.date, str]] = Field(
default=None,
description=(
"ISO date (YYYY-MM-DD). Only return sources published on or after "
"this date."
),
)

@field_validator("after_date", mode="before")
@classmethod
def _parse_after_date(cls, v: object) -> object:
if v is None or isinstance(v, _dt.date):
return v
if isinstance(v, str):
try:
return _dt.date.fromisoformat(v)
except ValueError as e:
msg = f"after_date must be ISO YYYY-MM-DD; got {v!r} ({e!s})."
raise ValueError(msg) from e
return v

@model_validator(mode="after")
def _check_domain_total(self) -> SourcePolicy:
total = len(self.include_domains or []) + len(self.exclude_domains or [])
if total > 200:
msg = (
f"Combined include_domains + exclude_domains has {total} entries; "
f"the API caps the total at 200."
)
raise ValueError(msg)
return self
"""Back-compat shim for the renamed :mod:`langchain_parallel.types` module.

The settings classes (``ExcerptSettings``, ``FetchPolicy``, ``FullContentSettings``,
``SourcePolicy``) live in :mod:`langchain_parallel.types` as of 0.4.1. This shim
re-exports them so any code that imported from ``langchain_parallel._types``
keeps working; new code should import from ``langchain_parallel.types`` (or
the package root) directly.
"""

from langchain_parallel.types import (
ExcerptSettings,
FetchPolicy,
FullContentSettings,
SourcePolicy,
)

__all__ = [
"ExcerptSettings",
"FetchPolicy",
"FullContentSettings",
"SourcePolicy",
]
22 changes: 11 additions & 11 deletions langchain_parallel/chat_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Parallel Web chat model integration.
"""ChatParallel chat model integration.

This module provides the ChatParallelWeb class for interacting with Parallel's
This module provides the ChatParallel class for interacting with Parallel's
Chat API through an OpenAI-compatible interface.
"""

Expand Down Expand Up @@ -165,8 +165,8 @@ def _merge_consecutive_messages(messages: list[BaseMessage]) -> list[BaseMessage
return merged


class ChatParallelWeb(BaseChatModel):
"""Parallel Web chat model integration.
class ChatParallel(BaseChatModel):
"""ChatParallel chat model integration.

This integration connects to Parallel's Chat API, which provides
real-time web research capabilities through an OpenAI-compatible interface.
Expand Down Expand Up @@ -201,9 +201,9 @@ class ChatParallelWeb(BaseChatModel):

Instantiate:
```python
from langchain_parallel import ChatParallelWeb
from langchain_parallel import ChatParallel

llm = ChatParallelWeb(
llm = ChatParallel(
model="speed",
temperature=0.7,
max_tokens=None,
Expand Down Expand Up @@ -664,7 +664,7 @@ def with_structured_output(
f"Structured output requires one of the research models "
f"({sorted(_STRUCTURED_OUTPUT_MODELS)}); the '{self.model}' "
f"model silently ignores response_format. Re-instantiate with "
f"`ChatParallelWeb(model='lite' | 'base' | 'core')`."
f"`ChatParallel(model='lite' | 'base' | 'core')`."
)
raise ValueError(msg)
if method == "function_calling":
Expand Down Expand Up @@ -734,8 +734,8 @@ def _parse_with_capture(raw: AIMessage) -> dict[str, Any]:
return bound | output_parser


#: Forward-compat alias for :class:`ChatParallelWeb`.
#: Back-compat alias for :class:`ChatParallel`.
#:
#: Prefer ChatParallel in new code; ChatParallelWeb will continue to
#: work indefinitely as an alias for this class.
ChatParallel = ChatParallelWeb
#: ``ChatParallelWeb`` is the legacy class name and continues to work
#: indefinitely; new code should prefer ``ChatParallel``.
ChatParallelWeb = ChatParallel
2 changes: 1 addition & 1 deletion langchain_parallel/extract_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from pydantic import BaseModel, Field, SecretStr, model_validator

from ._client import get_api_key, get_async_parallel_client, get_parallel_client
from ._types import ExcerptSettings, FetchPolicy, FullContentSettings
from .types import ExcerptSettings, FetchPolicy, FullContentSettings


def _coerce_full_content(
Expand Down
2 changes: 1 addition & 1 deletion langchain_parallel/retrievers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pydantic import Field, SecretStr, model_validator

from ._client import get_api_key, get_async_parallel_client, get_parallel_client
from ._types import ExcerptSettings, FetchPolicy, SourcePolicy
from .types import ExcerptSettings, FetchPolicy, SourcePolicy


def _join_excerpts(excerpts: Optional[list[str]]) -> str:
Expand Down
18 changes: 9 additions & 9 deletions langchain_parallel/search_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pydantic import BaseModel, Field, SecretStr, field_validator, model_validator

from ._client import get_api_key, get_async_parallel_client, get_parallel_client
from ._types import ExcerptSettings, FetchPolicy, SourcePolicy
from .types import ExcerptSettings, FetchPolicy, SourcePolicy

_VALID_MODES = {"basic", "advanced"}

Expand Down Expand Up @@ -180,7 +180,7 @@ def _check_query_length(cls, qs: list[str]) -> list[str]:
)


class ParallelWebSearchTool(BaseTool):
class ParallelSearchTool(BaseTool):
"""Parallel Search tool with web research capabilities.

This tool calls Parallel's Search API, which streamlines the traditional
Expand All @@ -206,9 +206,9 @@ class ParallelWebSearchTool(BaseTool):

Instantiation:
```python
from langchain_parallel import ParallelWebSearchTool
from langchain_parallel import ParallelSearchTool

tool = ParallelWebSearchTool()
tool = ParallelSearchTool()
```

Invocation:
Expand Down Expand Up @@ -292,7 +292,7 @@ class ParallelWebSearchTool(BaseTool):
"""Asynchronous Parallel SDK client (initialized after validation)."""

@model_validator(mode="after")
def validate_environment(self) -> ParallelWebSearchTool:
def validate_environment(self) -> ParallelSearchTool:
"""Validate the environment and initialize SDK clients."""
api_key_str = get_api_key(
self.api_key.get_secret_value() if self.api_key else None,
Expand Down Expand Up @@ -534,8 +534,8 @@ async def _arun(
return response


#: Forward-compat alias for :class:`ParallelWebSearchTool`.
#: Back-compat alias for :class:`ParallelSearchTool`.
#:
#: Prefer ParallelSearchTool in new code; ParallelWebSearchTool will
#: continue to work indefinitely as an alias for this class.
ParallelSearchTool = ParallelWebSearchTool
#: ``ParallelWebSearchTool`` is the legacy class name and continues to
#: work indefinitely; new code should prefer ``ParallelSearchTool``.
ParallelWebSearchTool = ParallelSearchTool
Loading
Loading