22- **Backend**: Hono API framework
23- **Database**: SQLite for recipe storage
24- **AI**: OpenAI GPT-4 for intelligent recipe parsing
25- **File Processing**: PDF parsing and image OCR capabilities
26
1// val.js
2
3const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // set this later
4
5export default async (req, res) => {
1# Plant Information API
2
3A REST API that provides detailed plant information using OpenAI's GPT model with intelligent caching for improved performance and reduced API costs.
4
5## Project Structure
19- Get comprehensive plant information by name
20- Structured JSON response with 8 key plant characteristics
21- Powered by OpenAI GPT-4o-mini for accurate plant data
22- **Intelligent caching system** - stores successful responses and serves cached data for repeated requests
23- Cache management endpoints for monitoring and administration
46
47### GET /plant/:name
48Returns detailed information about a specific plant. If the plant information is already cached, returns the cached response immediately. Otherwise, fetches new information from OpenAI and caches it for future requests.
49
50**Parameters:**
68
69**Cache Indicators:**
70- `_cached`: Boolean indicating if the response came from cache (true) or OpenAI (false)
71- `_cacheTimestamp`: ISO timestamp of when the response was generated
72
174- `200`: Success
175- `400`: Bad request (missing plant name)
176- `500`: Server error (OpenAI API issues, parsing errors)
177
178Error responses include descriptive error messages and may include additional debugging information.
216- **Plant name normalization**: Plant names are normalized (lowercased, special characters removed, spaces converted to underscores) for consistent caching
217- **Cache hits**: Subsequent requests for the same plant (even with different capitalization or spacing) will return cached responses instantly
218- **Cache misses**: New plants trigger OpenAI API calls and the responses are automatically cached
219- **Performance**: Cached responses are served in milliseconds vs. seconds for OpenAI API calls
220- **Cost efficiency**: Reduces OpenAI API usage and associated costs
221
222## Technical Details
224- **Main entry point**: `index.ts` at project root with HTTP trigger
225- Built with Hono framework
226- Uses OpenAI GPT-4o-mini model
227- SQLite database for caching with automatic table creation
228- Includes input validation and error handling
1import { Hono } from "https://esm.sh/hono@3.11.7";
2import { OpenAI } from "https://esm.town/v/std/openai";
3import { readFile } from "https://esm.town/v/std/utils/index.ts";
4import { sqlite } from "https://esm.town/v/stevekrouse/sqlite";
20});
21
22// Initialize OpenAI
23const openai = new OpenAI();
24
25// Initialize cache on startup with error handling
88 }
89
90 console.log(`Fetching fresh data from OpenAI for: "${plantName}"`);
91 // If not cached, fetch from OpenAI
92 const prompt = `Please provide detailed information about the plant "${plantName}" in the following JSON format. Be specific and accurate:
93
105Only return the JSON object, no additional text.`;
106
107 const completion = await openai.chat.completions.create({
108 messages: [
109 { role: "user", content: prompt }
117
118 if (!responseText) {
119 return c.json({ error: "No response from OpenAI" }, 500);
120 }
121
129 }
130
131 // Parse the JSON response from OpenAI
132 const plantInfo: PlantInfo = JSON.parse(cleanedResponse);
133
143Note: When changing a SQLite table's schema, change the table's name (e.g., add _2 or _3) to create a fresh table.
144
145### OpenAI
146```ts
147import { OpenAI } from "npm:openai";
148const openai = new OpenAI();
149const completion = await openai.chat.completions.create({
150 messages: [
151 { role: "user", content: "Say hello in a creative way" },
86Note: When changing a SQLite table's schema, change the table's name (e.g., add _2 or _3) to create a fresh table.
87
88### OpenAI
89
90```ts
91import { OpenAI } from "https://esm.town/v/std/openai";
92const openai = new OpenAI();
93const completion = await openai.chat.completions.create({
94 messages: [
95 { role: "user", content: "Say hello in a creative way" },
83
84 <library>
85 ## OpenAI
86
87 Val Town includes a free, proxied OpenAI:
88
89 ```ts
90 import { OpenAI } from "https://esm.town/v/std/openai";
91 const openai = new OpenAI();
92 const completion = await openai.chat.completions.create({
93 messages: [
94 { role: "user", content: "Say hello in a creative way" },
99 ```
100
101 OpenAI only works on the server. If the val includes client-side code, use dynamic imports to import this module in the server function, e.g.:
102 `const { OpenAI } = await import "https://esm.town/v/std/openai");`
103 </library>
104
71```
72
73### OpenAI Integration
74```typescript
75import { OpenAI } from "https://esm.town/v/std/openai";
76const openai = new OpenAI();
77const completion = await openai.chat.completions.create({
78 messages: [{ role: "user", content: "Say hello" }],
79 model: "gpt-4o-mini",
11 console.log("Received:", message, "from", from);
12
13 // Get OpenAI API key
14 const openaiKey = Deno.env.get("OPENAI_API_KEY")!;
15
16 // 1. Ask OpenAI for a response
17 let aiResponse = "Sorry, something went wrong.";
18 try {
19 const openaiRes = await fetch("https://api.openai.com/v1/chat/completions", {
20 method: "POST",
21 headers: {
22 Authorization: `Bearer ${openaiKey}`,
23 "Content-Type": "application/json",
24 },
39 });
40
41 const data = await openaiRes.json();
42 aiResponse = data.choices?.[0]?.message?.content ?? aiResponse;
43 } catch (err) {
44 console.error("OpenAI error:", err);
45 }
46
94Note: When changing a SQLite table's schema, change the table's name (e.g., add _2 or _3) to create a fresh table.
95
96### OpenAI
97
98```ts
99import { OpenAI } from "https://esm.town/v/std/openai";
100const openai = new OpenAI();
101const completion = await openai.chat.completions.create({
102 messages: [
103 { role: "user", content: "Say hello in a creative way" },