Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions ai/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# AI related snippets

# MCP
## MCP

Every API Connect for GraphQL deployed schema has an MCP `/mcp` endpoint
in addition to its `/graphql` endpoint.

Articles:
### Articles

- https://developer.ibm.com/articles/awb-simplifying-llm-integration-mcp-api-connect-graphql/
- https://community.ibm.com/community/user/blogs/timil-titus/2025/10/12/mcp-and-ibm-api-connect-for-graphql

Snippets:
### Snippets

- [Prescribed tools](../executable/prescribed/) - Demonstrates creation of MCP tools from persisted GraphQL operations.

## AI snippets

- [watsonx.AI generation](watsonxai-generation/) - Demonstrates invoking an LLM through watsonx.AI within a GraphQL endpoint.
55 changes: 55 additions & 0 deletions ai/watsonxai-generation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# watsonx.ai generation.

## Overview

Creates a field `Query.wxai_generation` that invokes a watsonx.ai generation endpoint to infer the next text from an input.

This provides a simple example of calling an LLM from a GraphQL endpoint.

An IBM Cloud IAM token is used for authorization, taken from the [ibm-iam snippet](../../rest/ibm-iam/).

## Try it out

### Deploying

Deploy the schema with this values set in `.env` for your watsonx.ai project.

```
STEPZEN_IBM_IAM_APIKEY=
STEPZEN_WATSONXAI_URL=
STEPZEN_WATSONXAI_PROJECTID=
```

These can be obtained from the watsonx dashboard:

<img width="362" height="452" alt="image" src="https://github.com/user-attachments/assets/1eaa1671-2e2c-4a34-8ae7-1fd42ce9887e" />

### Sample request

Then you can use `stepzen request` and the provided operation to ask simple questions:

```
stepzen request --file op.graphql --var question="when was hadrian's wall built"
```

recieving a GraphQL response like:

```
{
"data": {
"wxai_generation": {
"created_at": "2025-11-12T18:29:23.65Z",
"model_id": "ibm/granite-3-8b-instruct",
"model_version": "1.1.0",
"results": [
{
"generated_text": "\n\nHadrian's Wall was built between 122 and 128 AD",
"generated_token_count": 20,
"input_token_count": 7,
"stop_reason": "max_tokens"
}
]
}
}
}
```
10 changes: 10 additions & 0 deletions ai/watsonxai-generation/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
configurationset:
- configuration:
name: ibm-iam
endpoint: https://iam.cloud.ibm.com/identity/token
apikey: STEPZEN_IBM_IAM_APIKEY
- configuration:
name: watsonx-service
version: '2024-06-28'
url: STEPZEN_WATSONXAI_URL
project_id: STEPZEN_WATSONXAI_PROJECTID
33 changes: 33 additions & 0 deletions ai/watsonxai-generation/ibm-iam.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
extend type Query {
"""
Obtain an IBM Cloud bearer token.

Uses the [IBM Cloud API Key](https://cloud.ibm.com/docs/account?topic=account-userapikey&interface=ui#userapikey)
or [service ID's API Key](https://cloud.ibm.com/docs/account?topic=account-serviceidapikeys&interface=ui)
to [generate an IAM Token](https://cloud.ibm.com/docs/account?topic=account-iamtoken_from_apikey#iamtoken_from_apikey)
"""
ibm_iam_token: Secret
@rest(
endpoint: "$endpoint"
method: POST
contenttype: "x-www-form-urlencoded"
postbody: """
grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={{ .Get "apikey"}}
"""
ecmascript: """
function transformREST(body) {
switch (status) {
case 200:
return body
case 401:
case 400: // returned for apikey not found
throw new Error('unauthorized');
default:
throw new Error('unknown error');
}
}
"""
setters: { path: "access_token" }
configuration: "ibm-iam"
)
}
13 changes: 13 additions & 0 deletions ai/watsonxai-generation/index.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
Snippet demonstrating calling Watsonx.AI's generation endpoint.
"""
schema
@sdl(
files: ["watsonx-ai.graphql", "ibm-iam.graphql"]
# Only expose wxai_ fields
visibility: { expose: true, types: "Query", fields: "wxai_.*" }
# ensure the operation remains valid
executables: { document: "op.graphql" }
) {
query: Query
}
13 changes: 13 additions & 0 deletions ai/watsonxai-generation/op.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
query AskWatsonxAI($question: String!) {
wxai_generation(input: $question) {
created_at
model_id
model_version
results {
generated_text
generated_token_count
input_token_count
stop_reason
}
}
}
3 changes: 3 additions & 0 deletions ai/watsonxai-generation/stepzen.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"endpoint": "api/miscellaneous"
}
11 changes: 11 additions & 0 deletions ai/watsonxai-generation/tests/Test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const {
deployAndRun,
getTestDescription,
} = require("../../../tests/gqltest.js");

testDescription = getTestDescription("snippets", __dirname);

describe(testDescription, function () {
const tests = [];
return deployAndRun(__dirname, tests);
});
98 changes: 98 additions & 0 deletions ai/watsonxai-generation/watsonx-ai.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
extend type Query {
"""
Infers the next tokens for a given deployed model from the `input` text.
"""
wxai_generation(
input: String
model_id: String! = "ibm/granite-3-8b-instruct"
parameters: WXAI_GenerationParameters
): WXAI_GenerationResponse
@sequence(
steps: [
{ query: "ibm_iam_token" }
{
query: "_wxai_generation"
arguments: [
{ name: "token", field: "§0" }
{ name: "input", argument: "input" }
{ name: "model_id", argument: "model_id" }
{ name: "parameters", argument: "parameters" }
]
}
]
)

"""
Calls the generation endpoint.
"""
_wxai_generation(
token: Secret!
input: String
model_id: String
parameters: WXAI_GenerationParameters
): WXAI_GenerationResponse
@rest(
method: POST
endpoint: "$url;"
path: "/ml/v1/text/generation?version=$version"
headers: [{ name: "authorization", value: "Bearer $token" }]
ecmascript: """
function bodyPOST(s) {
let body = JSON.parse(s)
body.project_id = get("project_id")
return JSON.stringify(body)
}
"""
configuration: "watsonx-service"
)
}

"""
The parameters for the model of a generative AI request. See https://cloud.ibm.com/apidocs/watsonx-ai#text-generation for more details.
"""
input WXAI_GenerationParameters {
"""
The temperature specifies the amount of variation in the generation process when using sampling mode.
"""
temperature: Float = 0
"""
The strategy the model uses to choose the tokens in the generated output.
"""
decoding_method: String
"""
The minimum number of new tokens to be generated.
"""
min_new_tokens: Int
"""
The maximum number of new tokens to be generated.
"""
max_new_tokens: Int = 20
"""
The repetition penalty lowers the probability scores of tokens that have already been generated or belong to the context.
"""
repetition_penalty: Float
"""
A string of one or more characters which will stop the text generation if/when they appear in the generated output.
"""
stop_sequences: [String]
}

"""
Response of IBM watsonx.ai `generation` endpoint
"""
type WXAI_GenerationResponse {
created_at: DateTime
model_id: String
model_version: String
results: [WXAI_GenerationResult]
}

"""
A result in the response of IBM watsonx.ai `generation` endpoint
"""
type WXAI_GenerationResult {
generated_text: String
generated_token_count: Int
input_token_count: Int
stop_reason: String
}