Skip to main content

An integration package connecting Moonshot AI and LangChain

Project description

LangChain Moonshot

langchain-moonshot is a standalone LangChain integration package for Moonshot AI chat models.

Upgrade Notes

0.1.0 is a breaking rewrite of the package.

  • If you are upgrading from 0.0.6 or 0.0.7, note that the old PyPI releases exposed OpenAI, ChatOpenAI, and OpenAIEmbeddings.
  • Starting with 0.1.0, this project is rebuilt around ChatMoonshot as the primary public integration.
  • The project homepage/repository has moved from https://github.com/RyanFeiluX/langchain_moonshot to https://github.com/ArcadiaLin/langchain-moonshot.

It provides a ChatMoonshot implementation built on top of langchain-openai, with Moonshot-specific support for:

  • reasoning_content in non-streaming and streaming responses
  • thinking, prompt_cache_key, safety_identifier, and max_completion_tokens
  • Moonshot model capability profiles
  • tool calling, structured output, and multimodal inputs supported by Moonshot models

Overview

Integration details

Class Package Status Notes
ChatMoonshot langchain-moonshot alpha Standalone provider package for Moonshot AI

Model features

Tool calling Structured output Image input Video input Token-level streaming Native async Token usage Reasoning traces

Installation

Install from PyPI:

pip install langchain-moonshot

For local development:

uv sync --group dev --group test_integration

Standard Tests

This repository keeps only LangChain standard chat-model tests under tests/:

  • tests/unit_tests/test_standard.py
  • tests/integration_tests/test_standard.py

Run the full standard suite in the current uv project and write the output to pytest_standard.txt:

RUN_MOONSHOT_INTEGRATION=1 uv run pytest tests/unit_tests/test_standard.py tests/integration_tests/test_standard.py -q > pytest_standard.txt 2>&1

Run only the unit standard tests:

uv run pytest tests/unit_tests/test_standard.py -q > pytest_standard.txt 2>&1

Run only the live integration standard tests:

RUN_MOONSHOT_INTEGRATION=1 uv run pytest tests/integration_tests/test_standard.py -q > pytest_standard.txt 2>&1

Live integration tests require RUN_MOONSHOT_INTEGRATION=1 and MOONSHOT_API_KEY.

Current standard-test coverage and behavior:

  • Default live standard model: kimi-k2.5 with thinking=False and temperature=0.6
  • All standard tests in this repository are constrained to kimi-k2.5; the suite does not switch to any other Moonshot model
  • Covered by standard tests: serialization, env-based init, tool calling, structured output, json_mode, image inputs, streaming, async, and VCR-backed test_stream_time
  • Standard tests intentionally keep these capabilities unsupported: forced tool_choice, runtime model override, PDF inputs, audio inputs, image/PDF tool messages, and Anthropic-style inputs
  • Known expected limitation: output_version="v1" streaming tool calls can emit a natural-language preamble before the tool chunk stream, so the standard test_tool_calling path for that variant is marked xfail
  • Practical boundary: for day-to-day use, prefer invoke() or default streaming output for tool calling; do not rely on output_version="v1" streamed content_blocks as the sole source of tool-call arguments

Credentials

Create a Moonshot API key in the Moonshot console, then set MOONSHOT_API_KEY.

import getpass
import os

if not os.getenv("MOONSHOT_API_KEY"):
    os.environ["MOONSHOT_API_KEY"] = getpass.getpass("Enter your Moonshot API key: ")

If you are using Moonshot's China endpoint explicitly, you can also set:

os.environ.setdefault("MOONSHOT_API_BASE", "https://api.moonshot.cn/v1")

To enable LangSmith tracing:

os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = getpass.getpass("Enter your LangSmith API key: ")

Instantiation

from langchain_moonshot import ChatMoonshot

llm = ChatMoonshot(
    model="kimi-k2.5",
    thinking=True,
    temperature=1.0,
    max_retries=2,
)

Invocation

messages = [
    ("system", "You are a concise bilingual assistant."),
    ("human", "Summarize why Moonshot reasoning models are useful in two bullet points."),
]

ai_msg = llm.invoke(messages)

print(ai_msg.text)
print(ai_msg.additional_kwargs.get("reasoning_content"))
print(ai_msg.usage_metadata)

Streaming

streaming_llm = ChatMoonshot(
    model="kimi-k2.5",
    thinking=True,
    temperature=1.0,
    stream_usage=True,
)

full = None
for chunk in streaming_llm.stream("Explain streaming output in two short bullet points."):
    if full is None:
        full = chunk
    else:
        full += chunk

    if chunk.text:
        print(chunk.text, end="")

    reasoning = chunk.additional_kwargs.get("reasoning_content")
    if reasoning:
        print(f"\n[reasoning] {reasoning}", end="")

print()
print(full.usage_metadata if full is not None else None)

Tool Calling

from langchain_core.messages import ToolMessage
from langchain_core.tools import tool


@tool
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b


@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b


llm_with_tools = ChatMoonshot(
    model="kimi-k2.5",
    thinking=False,
    temperature=0.6,
).bind_tools([add, multiply])

messages = [
    ("system", "Use the provided math tools before answering."),
    ("human", "Add 17 and 25, multiply 12 by 13, then summarize the results."),
]

response = llm_with_tools.invoke(messages)
print(response.tool_calls)

if response.tool_calls:
    tool_results = []
    for tool_call in response.tool_calls:
        if tool_call["name"] == "add":
            result = add.invoke(tool_call["args"])
        else:
            result = multiply.invoke(tool_call["args"])
        tool_results.append(
            ToolMessage(content=str(result), tool_call_id=tool_call["id"])
        )

    final_response = llm_with_tools.invoke([*messages, response, *tool_results])
    print(final_response.text)

Structured Output

Moonshot does not expose a distinct json_schema steering path in this package. ChatMoonshot.with_structured_output(..., method="json_schema") is intentionally downgraded to function_calling.

from pydantic import BaseModel, Field


class WeatherAnswer(BaseModel):
    city: str = Field(description="City name")
    summary: str = Field(description="One-sentence weather summary")


structured_llm = ChatMoonshot(
    model="kimi-k2.5",
    thinking=False,
    temperature=0.6,
).with_structured_output(WeatherAnswer)

result = structured_llm.invoke("Summarize today's weather in Shanghai.")
print(result)

Multimodal Input

Vision-capable Moonshot models accept OpenAI-style image_url content blocks.

from langchain_core.messages import HumanMessage

vision_llm = ChatMoonshot(
    model="moonshot-v1-32k-vision-preview",
)

message = HumanMessage(
    content=[
        {"type": "text", "text": "Describe the image and mention one concrete detail."},
        {
            "type": "image_url",
            "image_url": {"url": "data:image/png;base64,<your-base64-image>"},
        },
    ]
)

response = vision_llm.invoke([message])
print(response.text)

LangGraph Compatibility

ChatMoonshot works as a normal LangChain chat model inside LangGraph agent loops.

from typing import Annotated, Sequence, TypedDict

from langchain_core.messages import BaseMessage
from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode


class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]


tools = [add, multiply]
model = ChatMoonshot(
    model="kimi-k2.5",
    thinking=False,
    temperature=0.6,
).bind_tools(tools)


def model_call(state: AgentState) -> AgentState:
    response = model.invoke([("system", "Use tools when needed."), *state["messages"]])
    return {"messages": [response]}


def should_continue(state: AgentState) -> str:
    last = state["messages"][-1]
    return "continue" if getattr(last, "tool_calls", None) else "end"


graph = StateGraph(AgentState)
graph.add_node("agent", model_call)
graph.add_node("tools", ToolNode(tools=tools))
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue, {"continue": "tools", "end": END})
graph.add_edge("tools", "agent")

result = graph.compile().invoke(
    {"messages": [("user", "Add 3 and 4, multiply 12 by 13, then summarize.")]}
)
print(result["messages"][-1].content)

Moonshot-Specific Notes

  • kimi-k2.5 is validated more strictly than generic OpenAI-compatible models.
  • When thinking=True, kimi-k2.5 expects temperature=1.0.
  • When thinking=False, kimi-k2.5 expects temperature=0.6.
  • For kimi-k2.5, top_p must remain 0.95, n must remain 1, and both penalties must remain 0.0.
  • The integration accepts standard tool_choice values for API compatibility.
  • Moonshot does not reliably force tool calls in live tests, so forced tool_choice is treated as unsupported in standard tests.
  • For kimi-k2.5 with thinking=True, tool forcing still must remain tool_choice="auto" or "none".
  • Moonshot builtin $web_search is rejected when thinking=True.

Local Verification

uv run ruff check .
uv run mypy langchain_moonshot tests
uv run pytest tests/unit_tests/test_standard.py
RUN_MOONSHOT_INTEGRATION=1 uv run pytest tests/integration_tests/test_standard.py
RUN_MOONSHOT_INTEGRATION=1 uv run pytest tests/unit_tests/test_standard.py tests/integration_tests/test_standard.py -q > pytest_standard.txt 2>&1

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langchain_moonshot-0.1.0.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langchain_moonshot-0.1.0-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file langchain_moonshot-0.1.0.tar.gz.

File metadata

  • Download URL: langchain_moonshot-0.1.0.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for langchain_moonshot-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0d98b5ca0f707b631a138008087815990b8fc7e2cbc369205724ed5d322d056a
MD5 6fa667734cb4b6f1cf30b6c9e7a8a8ae
BLAKE2b-256 7edc4fbcb89c06259a18eb04f9d9229e6fc938011c060b82791e6e45d85f60e1

See more details on using hashes here.

File details

Details for the file langchain_moonshot-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: langchain_moonshot-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for langchain_moonshot-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1f62d84ae7b0dcdd544c643778149437a632acd034c7f0e9e148f55285bf5796
MD5 d365451d2338848f8854174b3ae3347d
BLAKE2b-256 f02fcfab3472126a3532ddd3f68bf37205971d3fce1a7ec5aec2194b19bf3a40

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page