diff --git a/.env.example b/.env.example index 43fb21f..e0edf2d 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,17 @@ SCALEKIT_CLIENT_ID=skc_your_client_id_here SCALEKIT_CLIENT_SECRET=skcs_your_client_secret_here SCALEKIT_ENVIRONMENT_URL=https://your-subdomain.scalekit.dev +# LangSmith tracing (for langsmith_tracing.py example). +# Get your API key from: https://smith.langchain.com/settings +LANGCHAIN_TRACING_V2=true +LANGCHAIN_API_KEY=lsv2_your_langsmith_api_key_here +LANGCHAIN_PROJECT=scalekit-langsmith-test + +# LiteLLM proxy (used by LangChain and other framework examples). +LITELLM_BASE_URL= +LITELLM_API_KEY= +LITELLM_MODEL=claude-sonnet-4-6 + # Optional model provider settings for framework examples. OPENAI_BASE_URL= OPENAI_API_KEY= diff --git a/README.md b/README.md index 56c7d6a..4d4637d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,13 @@ These examples are the framework-oriented AgentKit samples migrated from the doc |----------|------| | Quickstart | [python/frameworks/quickstart/main.py](python/frameworks/quickstart/main.py) | | LangChain | [python/frameworks/langchain/agent.py](python/frameworks/langchain/agent.py) | +| LangChain + LangSmith | [python/frameworks/langchain/langsmith_tracing.py](python/frameworks/langchain/langsmith_tracing.py) | + +#### LangSmith trace output + +Scalekit tool calls appear as native tool spans in LangSmith alongside LLM calls: + +![LangSmith trace showing Scalekit gmail_fetch_mails tool call](python/frameworks/langchain/images/langsmith-trace.png) | Google ADK | [python/frameworks/google-adk/agent.py](python/frameworks/google-adk/agent.py) | | Anthropic | [python/frameworks/anthropic/agent.py](python/frameworks/anthropic/agent.py) | | OpenAI-compatible | [python/frameworks/openai/agent.py](python/frameworks/openai/agent.py) | diff --git a/python/frameworks/langchain/images/langsmith-trace.png b/python/frameworks/langchain/images/langsmith-trace.png new file mode 100644 index 0000000..785cd70 Binary files /dev/null and b/python/frameworks/langchain/images/langsmith-trace.png differ diff --git a/python/frameworks/langchain/langsmith_tracing.py b/python/frameworks/langchain/langsmith_tracing.py new file mode 100644 index 0000000..58f5b53 --- /dev/null +++ b/python/frameworks/langchain/langsmith_tracing.py @@ -0,0 +1,110 @@ +""" +LangChain agent with Scalekit tools + LangSmith tracing. + +Demonstrates that Scalekit's native LangChain StructuredTool objects trace +automatically in LangSmith when LANGCHAIN_TRACING_V2=true. + +Run: python python/frameworks/langchain/langsmith_tracing.py + +Required env vars (.env at repo root): + SCALEKIT_ENVIRONMENT_URL SCALEKIT_CLIENT_ID SCALEKIT_CLIENT_SECRET + LITELLM_BASE_URL LITELLM_API_KEY + LANGCHAIN_TRACING_V2=true + LANGCHAIN_API_KEY (this is the LangSmith API key) + +Optional: + LITELLM_MODEL (default: "claude-sonnet-4-6") + LANGCHAIN_PROJECT (default: "scalekit-langsmith-test") +""" + +import os + +from dotenv import find_dotenv, load_dotenv + +load_dotenv(find_dotenv()) + +# ── Verify LangSmith tracing is configured ────────────────────────────────── + +tracing_enabled = os.getenv("LANGCHAIN_TRACING_V2", "").lower() == "true" +langsmith_key = os.getenv("LANGCHAIN_API_KEY", "") +project = os.getenv("LANGCHAIN_PROJECT", "scalekit-langsmith-test") + +if not tracing_enabled: + print("⚠️ LANGCHAIN_TRACING_V2 is not set to 'true'. Traces will NOT be sent to LangSmith.") + print(" Set LANGCHAIN_TRACING_V2=true in your .env to enable tracing.") +if not langsmith_key: + print("⚠️ LANGCHAIN_API_KEY is not set. Traces will NOT be sent to LangSmith.") + print(" Get your API key from https://smith.langchain.com/settings") +else: + print(f"✅ LangSmith tracing enabled — project: {project}") + +# ── Initialize Scalekit client ────────────────────────────────────────────── + +import scalekit.client + +scalekit_client = scalekit.client.ScalekitClient( + client_id=os.getenv("SCALEKIT_CLIENT_ID"), + client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), + env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), +) +actions = scalekit_client.actions + +# ── Connect user to Gmail ─────────────────────────────────────────────────── + +IDENTIFIER = "user_123" + +response = actions.get_or_create_connected_account( + connection_name="gmail", + identifier=IDENTIFIER, +) +if response.connected_account.status != "ACTIVE": + link = actions.get_authorization_link(connection_name="gmail", identifier=IDENTIFIER) + print("Authorize Gmail:", link.link) + input("Press Enter after authorizing...") +else: + print(f"✅ Gmail connected for {IDENTIFIER}") + +# ── Get native LangChain tools ────────────────────────────────────────────── + +tools = actions.langchain.get_tools( + identifier=IDENTIFIER, + connection_names=["gmail"], +) +tool_map = {t.name: t for t in tools} +print(f"✅ Loaded {len(tools)} LangChain tools: {[t.name for t in tools[:5]]}") + +# ── Run agent with LangSmith tracing ──────────────────────────────────────── + +from langchain_core.messages import HumanMessage, ToolMessage +from langchain_openai import ChatOpenAI + +model = os.getenv("LITELLM_MODEL", "claude-sonnet-4-6") +llm = ChatOpenAI( + model=model, + openai_api_base=os.getenv("LITELLM_BASE_URL"), + openai_api_key=os.getenv("LITELLM_API_KEY"), +).bind_tools(tools) +print(f"✅ Using model: {model} via LiteLLM") +messages = [HumanMessage("Fetch my last 3 unread emails and summarize them")] + +print("\n--- Running agent (traces sent to LangSmith) ---\n") + +tool_call_count = 0 +while True: + response = llm.invoke(messages) + messages.append(response) + if not response.tool_calls: + print(response.content) + break + for tc in response.tool_calls: + tool_call_count += 1 + print(f" 🔧 Tool call #{tool_call_count}: {tc['name']}") + result = tool_map[tc["name"]].invoke(tc["args"]) + messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"])) + +# ── Summary ───────────────────────────────────────────────────────────────── + +print(f"\n✅ Agent completed — {tool_call_count} tool call(s)") +if tracing_enabled and langsmith_key: + print(f"✅ Check traces at: https://smith.langchain.com/o/default/projects/p/{project}") + print(" (Open LangSmith → select your project → view the latest trace)") diff --git a/python/requirements.txt b/python/requirements.txt index ffe9405..3dff0e4 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -4,6 +4,7 @@ requests anthropic langchain langchain-openai +langsmith google-adk litellm openai