Skip to content
NewDeepSeek V4.1 Flash 1017 Uncensored is live, with 1M tokens of context.Read the launch
SDKs and frameworks

LangChain

Use XField models in LangChain and LangGraph with ChatOpenAI or ChatAnthropic, in Python and JavaScript.

LangChain needs no special integration. Point ChatOpenAI or ChatAnthropic at XField and everything built on chat models, including LangGraph agents, works as usual.

Install

Shell
pip install langchain langchain-openai langchain-anthropic

ChatOpenAI

The simplest route. It uses the Chat Completions API at https://xfield.cloud.brutal.ai/api/v1.

Python
import os
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(
    model="deepseek-v4.1-flash-1017-uncensored",
    base_url="https://xfield.cloud.brutal.ai/api/v1",
    api_key=os.environ["XFIELD_API_KEY"],
    reasoning_effort="high",
    timeout=300,
    max_retries=4,
)
 
reply = llm.invoke("Give me five test cases for a password-reset flow.")
print(reply.content)
 
for chunk in llm.stream("Summarize OWASP ASVS level 1 in one paragraph."):
    print(chunk.content, end="", flush=True)

reasoning_effort maps to each model's levels; "none" turns thinking off. ChatOpenAI does not surface the reasoning text from Chat Completions. If you need it, use ChatAnthropic.

ChatAnthropic

Use this when you want thinking blocks in the message, or when the rest of your stack already expects Anthropic-style content.

Python
import os
from langchain_anthropic import ChatAnthropic
 
llm = ChatAnthropic(
    model="glm-5.3-uncensored",
    base_url="https://xfield.cloud.brutal.ai/api",
    api_key=os.environ["XFIELD_API_KEY"],
    max_tokens=8000,
)
 
reply = llm.invoke("Rank these log sources by value for detecting lateral movement: DNS, NetFlow, Sysmon.")
for block in reply.content:
    if isinstance(block, dict) and block.get("type") == "thinking":
        print("[thinking]", block["thinking"][:200])
print(reply.text)

The base URL has no /v1; the Anthropic client adds it. When a model thinks, content is a list of blocks, so read the answer through reply.text rather than reply.content.

Tools

Both classes support bind_tools with plain functions, Pydantic models or JSON schemas.

Python
from langchain_core.tools import tool
 
@tool
def lookup_host(hostname: str) -> dict:
    """Look up a host in the lab inventory."""
    return {"hostname": hostname, "os": "Ubuntu 24.04", "owner": "red-team-lab"}
 
msg = llm.bind_tools([lookup_host]).invoke("What OS is lab-web-01?")
print(msg.tool_calls)

Agents with LangGraph

LangChain's agent builder runs on LangGraph and takes any chat model, so a tool-using agent on XField is a few lines:

Python
from langchain.agents import create_agent
 
agent = create_agent(
    model=ChatOpenAI(
        model="deepseek-v4.1-flash-1017-uncensored",
        base_url="https://xfield.cloud.brutal.ai/api/v1",
        api_key=os.environ["XFIELD_API_KEY"],
    ),
    tools=[lookup_host],
    system_prompt="You triage hosts in an isolated lab. Be brief.",
)
 
result = agent.invoke({"messages": [{"role": "user", "content": "Who owns lab-web-01?"}]})
print(result["messages"][-1].content)

On older versions, langgraph.prebuilt.create_react_agent does the same job with the same model object. DeepSeek V4.1 Flash is the usual pick for long agent loops; switch to glm-5.3-uncensored for planning-heavy graphs. Keep the system prompt and tool list fixed between turns so prompt caching does its work.

JavaScript

The JS packages take the same settings. In @langchain/openai, the base URL goes under configuration:

TypeScript
import { ChatOpenAI } from "@langchain/openai";
 
const llm = new ChatOpenAI({
  model: "deepseek-v4.1-flash-1017-uncensored",
  apiKey: process.env.XFIELD_API_KEY,
  configuration: { baseURL: "https://xfield.cloud.brutal.ai/api/v1" },
});
 
const reply = await llm.invoke("Name three signs of credential stuffing in auth logs.");
console.log(reply.content);

Model ids are exact; see models for the list and limits.