-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlangchain_tool.py
More file actions
124 lines (99 loc) · 4.23 KB
/
langchain_tool.py
File metadata and controls
124 lines (99 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""
LangChain tool for Headless Oracle V5.
Usage:
from integrations.langchain_tool import MarketStatusTool, MarketScheduleTool
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI
tools = [MarketStatusTool(api_key="ok_live_..."), MarketScheduleTool()]
agent = initialize_agent(tools, ChatOpenAI(), agent=AgentType.OPENAI_FUNCTIONS)
agent.run("Is NYSE open right now? Should I execute this trade?")
Requires:
pip install "headless-oracle[langchain]"
"""
from __future__ import annotations
from typing import Optional, Type
from langchain_core.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun
from pydantic import BaseModel, Field
from headless_oracle import OracleClient, verify
class _MicInput(BaseModel):
mic: str = Field(
default="XNYS",
description="Exchange MIC code. Supported: XNYS, XNAS, XLON, XJPX, XPAR, XHKG, XSES.",
)
class MarketStatusTool(BaseTool):
"""
LangChain tool that fetches a cryptographically signed market status receipt
from Headless Oracle V5.
Returns OPEN, CLOSED, HALTED, or UNKNOWN. Always treat UNKNOWN as CLOSED.
Verifies the Ed25519 signature before returning the result.
"""
name: str = "get_market_status"
description: str = (
"Prevents trades against closed, halted, or holiday-closed exchanges. "
"Get the current open/closed status of a stock exchange. "
"Input: a MIC code (e.g. XNYS for NYSE, XNAS for NASDAQ, XLON for LSE). "
"Returns the exchange status and whether it is safe to trade. "
"UNKNOWN must always be treated as CLOSED."
)
args_schema: Type[BaseModel] = _MicInput
api_key: Optional[str] = None
public_key: Optional[str] = None
base_url: str = "https://headlessoracle.com"
def _run(
self,
mic: str = "XNYS",
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
with OracleClient(api_key=self.api_key, base_url=self.base_url) as client:
try:
receipt = client.get_demo(mic) if not self.api_key else client.get_status(mic)
except Exception as e:
# Fail-closed: on any fetch error, report UNKNOWN
return f"status=UNKNOWN mic={mic} error={e} — treat as CLOSED, halt execution"
result = verify(receipt, public_key=self.public_key)
if not result.valid:
return (
f"status=UNKNOWN mic={mic} verify_failed={result.reason} "
f"— signature invalid, treat as CLOSED, halt execution"
)
status = receipt["status"]
safe_to_trade = status == "OPEN"
reason = receipt.get("reason", "")
return (
f"mic={mic} status={status} safe_to_trade={safe_to_trade}"
+ (f" reason={reason}" if reason else "")
+ f" issued_at={receipt['issued_at']} expires_at={receipt['expires_at']}"
)
class _ScheduleInput(BaseModel):
mic: str = Field(
default="XNYS",
description="Exchange MIC code. Supported: XNYS, XNAS, XLON, XJPX, XPAR, XHKG, XSES.",
)
class MarketScheduleTool(BaseTool):
"""
LangChain tool that fetches the next open and close times for a stock exchange
from Headless Oracle V5.
"""
name: str = "get_market_schedule"
description: str = (
"Get the next open and close times for a stock exchange. "
"Input: a MIC code (e.g. XNYS for NYSE). "
"Returns ISO 8601 UTC timestamps for next open and close events."
)
args_schema: Type[BaseModel] = _ScheduleInput
base_url: str = "https://headlessoracle.com"
def _run(
self,
mic: str = "XNYS",
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
with OracleClient(base_url=self.base_url) as client:
try:
data = client.get_schedule(mic)
except Exception as e:
return f"schedule_unavailable mic={mic} error={e}"
next_open = data.get("next_open", "unknown")
next_close = data.get("next_close", "unknown")
tz = data.get("timezone", "")
return f"mic={mic} next_open={next_open} next_close={next_close} timezone={tz}"