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:

  1. GET with query params + bearer auth — the canonical "fetch from a public API" shape. paramType = QUERY appends each parameter as a URL query string, AuthType.BEARER reads authTokenEnv and injects an Authorization: Bearer <token> header.
  2. 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.

  3. GET with path templating — endpoint = ".../forecast/{cityId}" substitutes into the URL at request time. Single-brace {param} syntax (not ${param}) — see WebService.endpoint() javadoc.
  4. POST with body + bearer auth + custom headers — paramType = BODY serialises params as JSON, AuthType.BEARER reads authTokenEnv and sets Authorization: Bearer <token>, custom @Headers pass through unmodified.
  5. Retry config — retryCount + retryBackoffMs let transient 5xx / network blips self-heal without touching the Role's source.

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 a main 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

The return 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 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 with paramType = QUERY + API-key auth. Each parameter value is URL-encoded and appended as a query parameter; the API key is read from OPENWEATHER_API_KEY and sent under the apiKeyHeader (defaults to X-API-Key but 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 in WebServiceExecutor.buildUrl before 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 = BODY serialises parameter values as a JSON request body; AuthType.BEARER reads the token env and emits Authorization: Bearer <token>; the @Header entries pass through verbatim (useful for upstream-required identifiers like X-Source or locale hints).