OpenAI / GPT

This guide shows how to connect GPT-4 to the Epanya marketplace using OpenAI function calling. You define Epanya operations as functions, and GPT decides autonomously when to search for products and when to purchase them.

What you'll build: A GPT-4 agent that accepts procurement requests in natural language and executes the full discover → evaluate → purchase flow using OpenAI's function calling API.

Function definitions

OpenAI functions follow the JSON Schema format. Define the three Epanya tools as entries in the tools array:

const EPANYA_FUNCTIONS = [
  {
    type: "function" as const,
    function: {
      name: "discover_products",
      description:
        "Search the Epanya marketplace for products. Use this to find compute " +
        "resources, APIs, datasets, AI models, tools, or agent labor services. " +
        "Returns a list of matching products with names, prices in USDC, and IDs.",
      parameters: {
        type: "object",
        properties: {
          q: {
            type: "string",
            description: "Free-text search query, e.g. 'GPU inference' or 'web scraping API'",
          },
          category: {
            type: "string",
            enum: ["compute", "datasets", "apis", "models", "tools", "physical", "agent_labor"],
            description: "Filter by product category",
          },
          max_price: {
            type: "number",
            description: "Maximum price in USDC (e.g. 2.5)",
          },
          sort: {
            type: "string",
            enum: ["price_asc", "price_desc", "rating", "newest"],
            description: "Sort order for results (default: rating)",
          },
          limit: {
            type: "number",
            description: "Max number of results to return (default 10, max 50)",
          },
        },
        required: [],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "purchase_product",
      description:
        "Purchase a product from the Epanya marketplace by its ID. Only call " +
        "this after discovering and evaluating products. The payment is made " +
        "automatically in USDC. Returns a transaction ID and service endpoint URL.",
      parameters: {
        type: "object",
        properties: {
          product_id: {
            type: "string",
            description: "The product UUID from discover_products results",
          },
        },
        required: ["product_id"],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "check_budget",
      description:
        "Check the agent's current USDC spend and remaining budget. Call " +
        "this before purchasing if you are unsure whether budget remains.",
      parameters: {
        type: "object",
        properties: {},
        required: [],
      },
    },
  },
];

Agent setup

Initialize the OpenAI client and the Epanya SDK side by side:

import OpenAI from "openai";
import { EpanyaClient, createTestSigner } from "@epanya/agent-sdk";

const openai = new OpenAI(); // reads OPENAI_API_KEY from env

const AGENT_WALLET = process.env.AGENT_WALLET_ADDRESS ?? "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";

const epanya = new EpanyaClient({
  walletAddress: AGENT_WALLET,
  apiUrl: "https://api.epanya.ai",
  signer: createTestSigner(AGENT_WALLET), // replace with real EIP-3009 signer in production
});

The tool executor maps OpenAI function names to Epanya SDK calls:

async function executeTool(name: string, args: Record<string, unknown>): Promise<string> {
  switch (name) {
    case "discover_products": {
      const products = await epanya.discover({
        q:        args.q        as string | undefined,
        category: args.category as string | undefined,
        maxPrice: args.max_price as number | undefined,
        sort:     args.sort     as string | undefined,
        limit:    (args.limit   as number | undefined) ?? 10,
      });
      if (!products.length) return "No products found matching those criteria.";
      return products.map((p, i) =>
        `${i + 1}. **${p.name}**\n` +
        `   Price: $${p.priceUsdc} USDC (${p.pricingModel})\n` +
        `   ID: ${p.id}\n` +
        `   ${p.description}`
      ).join("\n\n");
    }

    case "purchase_product": {
      const result = await epanya.purchase(args.product_id as string);
      return JSON.stringify({
        success:       true,
        transactionId: result.transactionId,
        endpointUrl:   result.product.endpointUrl,
        amountPaid:    result.amount + " USDC",
      });
    }

    case "check_budget": {
      const budget = await epanya.checkBudget();
      return JSON.stringify({
        spent:          budget.spent + " USDC",
        limit:          budget.budgetLimit ? budget.budgetLimit + " USDC" : "unlimited",
        remaining:      budget.remaining   ? budget.remaining   + " USDC" : "unlimited",
        budgetExceeded: budget.budgetExceeded,
      });
    }

    default:
      return `Error: unknown function ${name}`;
  }
}

Agentic loop

The standard OpenAI function-calling loop: send messages, execute any tool_calls in the response, append the results, and repeat until the model returns finish_reason: "stop".

async function runProcurementAgent(userRequest: string): Promise<string> {
  const messages: OpenAI.ChatCompletionMessageParam[] = [
    {
      role: "system",
      content:
        "You are an autonomous procurement agent. You have access to the Epanya marketplace " +
        "where you can discover and purchase compute resources, APIs, datasets, AI models, " +
        "and other digital services. When given a procurement request:\n" +
        "1. Check the budget if there is any uncertainty about remaining funds.\n" +
        "2. Search for products matching the requirements.\n" +
        "3. Evaluate the options and pick the best value.\n" +
        "4. Purchase the chosen product and report the service endpoint URL.",
    },
    { role: "user", content: userRequest },
  ];

  while (true) {
    const response = await openai.chat.completions.create({
      model:       "gpt-4o",
      messages,
      tools:       EPANYA_FUNCTIONS,
      tool_choice: "auto",
    });

    const choice = response.choices[0];
    messages.push(choice.message);

    // Done — return the final answer
    if (choice.finish_reason === "stop") {
      return choice.message.content ?? "";
    }

    // Execute all function calls in this turn
    if (choice.finish_reason === "tool_calls" && choice.message.tool_calls) {
      for (const toolCall of choice.message.tool_calls) {
        console.log(`[function] ${toolCall.function.name}`, toolCall.function.arguments);
        let result: string;
        try {
          const args = JSON.parse(toolCall.function.arguments) as Record<string, unknown>;
          result = await executeTool(toolCall.function.name, args);
        } catch (err) {
          result = `Error: ${err instanceof Error ? err.message : String(err)}`;
        }
        messages.push({
          role:         "tool",
          tool_call_id: toolCall.id,
          content:      result,
        });
      }
    }
  }
}

Call the agent:

const answer = await runProcurementAgent(
  "I need a translation API that supports 50+ languages. " +
  "Budget is $1.50 per request max. Find the best rated option and buy it."
);
console.log(answer);

Full code

import OpenAI from "openai";
import { EpanyaClient, createTestSigner } from "@epanya/agent-sdk";

const openai      = new OpenAI();
const WALLET      = process.env.AGENT_WALLET_ADDRESS ?? "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const epanya      = new EpanyaClient({
  walletAddress: WALLET,
  apiUrl: "https://api.epanya.ai",
  signer: createTestSigner(WALLET),
});

const TOOLS: OpenAI.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "discover_products",
      description: "Search the Epanya marketplace for products.",
      parameters: {
        type: "object",
        properties: {
          q:         { type: "string" },
          category:  { type: "string", enum: ["compute","datasets","apis","models","tools","physical","agent_labor"] },
          max_price: { type: "number" },
          sort:      { type: "string", enum: ["price_asc","price_desc","rating","newest"] },
          limit:     { type: "number" },
        },
        required: [],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "purchase_product",
      description: "Purchase a product by ID. Returns transactionId and endpointUrl.",
      parameters: {
        type: "object",
        properties: { product_id: { type: "string" } },
        required: ["product_id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "check_budget",
      description: "Check USDC spend vs budget limit.",
      parameters: { type: "object", properties: {}, required: [] },
    },
  },
];

async function runAgent(request: string): Promise<void> {
  const messages: OpenAI.ChatCompletionMessageParam[] = [
    { role: "system", content: "You are an autonomous procurement agent for the Epanya marketplace." },
    { role: "user",   content: request },
  ];

  while (true) {
    const res    = await openai.chat.completions.create({ model: "gpt-4o", messages, tools: TOOLS, tool_choice: "auto" });
    const choice = res.choices[0];
    messages.push(choice.message);

    if (choice.finish_reason === "stop") {
      console.log(choice.message.content);
      break;
    }

    for (const tc of choice.message.tool_calls ?? []) {
      const args   = JSON.parse(tc.function.arguments) as Record<string, unknown>;
      let result: string;
      try   { result = await executeTool(tc.function.name, args); }
      catch (e) { result = `Error: ${e}`; }
      messages.push({ role: "tool", tool_call_id: tc.id, content: result });
    }
  }
}

async function executeTool(name: string, args: Record<string, unknown>): Promise<string> {
  if (name === "discover_products") {
    const ps = await epanya.discover({ q: args.q as string, category: args.category as string, maxPrice: args.max_price as number, limit: (args.limit as number) ?? 10 });
    return ps.length ? ps.map((p,i) => `${i+1}. ${p.name} $${p.priceUsdc} — ID: ${p.id}`).join("\n") : "No products found.";
  }
  if (name === "purchase_product") {
    const r = await epanya.purchase(args.product_id as string);
    return JSON.stringify({ transactionId: r.transactionId, endpointUrl: r.product.endpointUrl, paid: r.amount + " USDC" });
  }
  if (name === "check_budget") {
    const b = await epanya.checkBudget();
    return JSON.stringify({ spent: b.spent, limit: b.budgetLimit ?? "unlimited", exceeded: b.budgetExceeded });
  }
  return `Unknown function: ${name}`;
}

runAgent("Find me the cheapest GPU inference endpoint available and purchase it.").catch(console.error);

Responses API variant

If you're using the newer OpenAI Responses API (/v1/responses), the tool schema is identical — only the invocation changes:

// Same TOOLS array as above
const response = await openai.responses.create({
  model:  "gpt-4o",
  input:  "Find a GPU inference endpoint under $2/request.",
  tools:  TOOLS,
});

// Handle tool calls from response.output
for (const item of response.output) {
  if (item.type === "function_call") {
    const args   = JSON.parse(item.arguments) as Record<string, unknown>;
    const result = await executeTool(item.name, args);
    // Submit result back via response.submit_tool_outputs(...)
  }
}