Class WeatherRole
java.lang.Object
com.tnsai.integration.scop.examples.WeatherRole
@RoleSpec(name="WeatherAgent",
description="Looks up current weather and forecasts via OpenWeatherMap, subscribes users to alert thresholds.",
version="0.1.0",
llm=@LLMSpec(provider=OPENAI,model="gpt-4o-mini",temperature=0.2f))
public class WeatherRole
extends Object
Reference example: declarative
@WebService REST action
composition (issue #170).
Demonstrates the four documented patterns for binding HTTP
endpoints to LLM-callable actions purely through annotations —
no imperative agent.tool(...), no per-request
OkHttpClient.newCall(...), no manual JSON parsing in the
Role:
- GET with query params + bearer auth —
the canonical "fetch from a public API" shape.
paramType = QUERYappends each parameter as a URL query string,AuthType.BEARERreadsauthTokenEnvand injects anAuthorization: Bearer <token>header. - GET with path templating —
endpoint = ".../forecast/{cityId}"substitutes into the URL at request time. Single-brace{param}syntax (not${param}) — seeWebService.endpoint()javadoc. - POST with body + bearer auth + custom headers —
paramType = BODYserialises params as JSON,AuthType.BEARERreadsauthTokenEnvand setsAuthorization: Bearer <token>, custom@Headers pass through unmodified. - Retry config —
retryCount+retryBackoffMslet transient 5xx / network blips self-heal without touching the Role's source.
Note on API_KEY auth — the
WebService annotation javadoc lists API_KEY as
a value, but AuthType only enumerates NO_AUTH,
BEARER, and BASIC. API_KEY is a
documentation drift (tracked separately for follow-up); for
key-style auth today, use BEARER (the API key goes in
Authorization: Bearer <key>) or bake the key into a
Header value.
How SCOP routes these
Every@ActionSpec(type = WEB_SERVICE) on a registered
Role is dispatched by WebServiceExecutor (in
tnsai-core/actions/executors/). The framework's
ActionExecutor routing table wires
WEB_SERVICE → WebServiceExecutor at construction
(see ActionExecutor.java:177); no consumer code needs to
register it. SCOPBridge.executeAction(role, name, params)
is the entry point for SCOP-driven invocations.
Running this
This class is a specification, not amain
entry point. To exercise it end-to-end:
Agent agent = AgentBuilder.create()
.role(new WeatherRole())
.llm(new OpenAIClient("gpt-4o-mini"))
.build();
// Pattern 1 — GET with query + API-key auth
Object now = agent.executeAction(
"getCurrentWeather",
Map.of("q", "Istanbul"));
// Pattern 2 — GET with path template
Object forecast = agent.executeAction(
"getForecastByCityId",
Map.of("cityId", "745044", "cnt", 5));
// Pattern 3 — POST with body + bearer + custom headers
Object subscription = agent.executeAction(
"subscribeToAlerts",
Map.of("city", "Istanbul", "thresholdC", 35));
Method bodies are deliberately stubs
Thereturn null (or trivial returns) in each action
body is a documented framework convention — WebServiceExecutor
never invokes the method. The Java signature exists purely so
parameter declarations and the LLM-tool schema can be
generated from it; the actual return value comes from JSON
deserialization of the HTTP response.- Since:
- 0.5.4 (issue #170 — first-class cookbook for WEB_SERVICE actions)
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptiongetCurrentWeather(String q, String units) Pattern 1 — GET withparamType = QUERY+ API-key auth.getForecastByCityId(String cityId, int cnt) Pattern 2 — GET with path templating ({cityId}).subscribeToAlerts(String city, int thresholdC) Pattern 3 — POST with body, bearer auth, custom static headers.
-
Constructor Details
-
WeatherRole
public WeatherRole()
-
-
Method Details
-
getCurrentWeather
@ActionSpec(type=WEB_SERVICE, description="Current weather conditions for a city, by name.", webService=@WebService(endpoint="https://api.openweathermap.org/data/2.5/weather",method=GET,paramType=QUERY,auth=BEARER,authTokenEnv="OPENWEATHER_API_KEY",timeout=5000,retryCount=2,retryBackoffMs=500)) public String getCurrentWeather(String q, String units) Pattern 1 — GET withparamType = QUERY+ API-key auth. Each parameter value is URL-encoded and appended as a query parameter; the API key is read fromOPENWEATHER_API_KEYand sent under theapiKeyHeader(defaults toX-API-Keybut explicitly set here for clarity). -
getForecastByCityId
@ActionSpec(type=WEB_SERVICE, description="N-day forecast for a city, addressed by its OpenWeatherMap city id.", webService=@WebService(endpoint="https://api.openweathermap.org/data/2.5/forecast/{cityId}",method=GET,paramType=QUERY,auth=BEARER,authTokenEnv="OPENWEATHER_API_KEY",timeout=7500)) public String getForecastByCityId(String cityId, int cnt) Pattern 2 — GET with path templating ({cityId}). The single-brace syntax is the framework's convention; the substitution happens inWebServiceExecutor.buildUrlbefore the request leaves the JVM. Remaining params are appended as query string (paramType = QUERY). -
subscribeToAlerts
@ActionSpec(type=WEB_SERVICE, description="Subscribe a user to weather-threshold alerts.", webService=@WebService(endpoint="https://api.example.com/alerts/subscribe",method=POST,paramType=BODY,contentType="application/json",accept="application/json",auth=BEARER,authTokenEnv="ALERTS_API_TOKEN",headers={,},timeout=10000)) public String subscribeToAlerts(String city, int thresholdC) Pattern 3 — POST with body, bearer auth, custom static headers.paramType = BODYserialises parameter values as a JSON request body;AuthType.BEARERreads the token env and emitsAuthorization: Bearer <token>; the@Headerentries pass through verbatim (useful for upstream-required identifiers likeX-Sourceor locale hints).
-