Skip to content

Commit 447fdc9

Browse files
authored
Merge pull request #10 from Unity-Lab-AI/codex/fix-pollinations-token-endpoint-error
Allow running without Pollinations token
2 parents 57019c7 + 551c628 commit 447fdc9

7 files changed

Lines changed: 381 additions & 84 deletions

README.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,27 @@ make sure the contents of `dist/` are deployed.
3333

3434
## Configuring the Pollinations token
3535

36-
Pollinations models that require tiered access need a token on every request. The application now
37-
expects the token to be provided at runtime so it is never bundled into the static assets.
36+
Pollinations models that require tiered access expect the token to be supplied as a request
37+
parameter. The demo can resolve the token at runtime (via URL parameters, meta tags, or injected
38+
globals) and also honours build-time environment variables when you want to bake the token into the
39+
bundle.
3840

3941
- **GitHub Pages / production** – Provide the `POLLI_TOKEN` secret in the repository (or Pages
40-
environment). The included Pages Function at `.github/functions/polli-token.js` exposes the token
41-
at runtime via `/api/polli-token`, and responses are marked as non-cacheable.
42-
- **Local development** – Either define `POLLI_TOKEN`/`VITE_POLLI_TOKEN` in your shell when running
43-
`npm run dev`, add a `<meta name="pollinations-token" ...>` tag to `index.html`, or inject
44-
`window.__POLLINATIONS_TOKEN__` before the application bootstraps.
45-
- **Static overrides** – When a dynamic endpoint is unavailable, append a `token` query parameter
46-
to the page URL (e.g. `https://example.github.io/chatdemo/?token=your-secret`). The application
47-
will capture the token, remove it from the visible URL, and apply it to subsequent Pollinations
48-
requests.
49-
50-
If the token cannot be resolved the UI remains disabled and an error is shown in the status banner.
42+
environment). You can surface the token to the client by setting `window.__POLLINATIONS_TOKEN__`,
43+
defining a `<meta name="pollinations-token" content="...">` tag, adding a `token=...` query
44+
parameter to the published URL (e.g. `https://example.github.io/chatdemo/?token=your-secret`), or
45+
injecting `POLLI_TOKEN`/`VITE_POLLI_TOKEN` during the build so the token ships with the bundle.
46+
The token is removed from the visible URL after it is captured.
47+
- **Local development** – Define `POLLI_TOKEN`/`VITE_POLLI_TOKEN` in your shell when running
48+
`npm run dev`, add a meta tag as above, or inject `window.__POLLINATIONS_TOKEN__` before the
49+
application bootstraps. Build-time environment variables also work in development.
50+
- **Optional runtime endpoint** – If you expose the token via a custom endpoint, configure its URL
51+
with `POLLI_TOKEN_ENDPOINT`/`VITE_POLLI_TOKEN_ENDPOINT` (environment variables),
52+
`window.__POLLINATIONS_TOKEN_ENDPOINT__`, or a `<meta name="pollinations-token-endpoint" ...>` tag.
53+
When present, the client will fetch the token from that endpoint.
54+
55+
If the token cannot be resolved the application continues without one, allowing you to browse public
56+
models while gated Pollinations models remain unavailable until a token is supplied.
57+
58+
All chat and image requests automatically include a random eight-digit `seed` parameter so they
59+
match Pollinations' expected request format.

src/main.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
matchesModelIdentifier,
77
normalizeTextCatalog,
88
} from './model-catalog.js';
9+
import { generateSeed } from './seed.js';
910

1011
const FALLBACK_MODELS = [
1112
createFallbackModel('openai', 'OpenAI GPT-5 Nano (fallback)'),
@@ -443,6 +444,7 @@ async function handleChatResponse(initialResponse, model, endpoint) {
443444
messages: state.conversation,
444445
tools: [IMAGE_TOOL],
445446
tool_choice: 'auto',
447+
seed: generateSeed(),
446448
},
447449
client,
448450
);
@@ -499,7 +501,7 @@ async function handleToolCalls(toolCalls) {
499501
const caption = String(args.caption ?? prompt).trim() || prompt;
500502

501503
try {
502-
const { dataUrl } = await generateImageAsset(prompt, {
504+
const { dataUrl, seed } = await generateImageAsset(prompt, {
503505
width,
504506
height,
505507
model: args.model,
@@ -520,6 +522,7 @@ async function handleToolCalls(toolCalls) {
520522
prompt,
521523
width,
522524
height,
525+
seed,
523526
}),
524527
});
525528
} catch (error) {
@@ -545,12 +548,14 @@ async function generateImageAsset(prompt, { width, height, model: imageModel } =
545548
if (!client) {
546549
throw new Error('Pollinations client is not ready.');
547550
}
551+
const seed = generateSeed();
548552
const binary = await image(
549553
prompt,
550554
{
551555
width,
552556
height,
553557
model: imageModel,
558+
seed,
554559
nologo: true,
555560
private: true,
556561
enhance: true,
@@ -559,7 +564,7 @@ async function generateImageAsset(prompt, { width, height, model: imageModel } =
559564
);
560565
const dataUrl = binary.toDataUrl();
561566
resetStatusIfIdle();
562-
return { dataUrl };
567+
return { dataUrl, seed };
563568
} catch (error) {
564569
console.error('Image generation failed', error);
565570
throw error;
@@ -770,13 +775,15 @@ async function requestChatCompletion(model, endpoints) {
770775
const attemptErrors = [];
771776
for (const endpoint of endpoints) {
772777
try {
778+
const requestSeed = generateSeed();
773779
const response = await chat(
774780
{
775781
model: model.id,
776782
endpoint,
777783
messages: state.conversation,
778784
tools: [IMAGE_TOOL],
779785
tool_choice: 'auto',
786+
seed: requestSeed,
780787
},
781788
client,
782789
);
@@ -907,11 +914,27 @@ async function initializeApp() {
907914
els.voicePlayback.checked = false;
908915
}
909916

917+
let tokenSource = null;
918+
let tokenMessages = [];
919+
910920
try {
911-
const { client: polliClient, tokenSource } = await createPollinationsClient();
921+
const {
922+
client: polliClient,
923+
tokenSource: resolvedTokenSource,
924+
tokenMessages: resolvedTokenMessages,
925+
} = await createPollinationsClient();
912926
client = polliClient;
927+
tokenSource = resolvedTokenSource;
928+
tokenMessages = Array.isArray(resolvedTokenMessages) ? resolvedTokenMessages : [];
913929
if (tokenSource) {
914930
console.info('Pollinations token loaded via %s.', tokenSource);
931+
} else if (tokenMessages.length) {
932+
console.warn(
933+
'Proceeding without a Pollinations token. Attempts: %s',
934+
tokenMessages.join('; '),
935+
);
936+
} else {
937+
console.info('Proceeding without a Pollinations token.');
915938
}
916939
} catch (error) {
917940
console.error('Failed to configure Pollinations client', error);
@@ -928,6 +951,10 @@ async function initializeApp() {
928951
setLoading(false);
929952
}
930953

954+
if (!tokenSource && !state.statusError) {
955+
setStatus('Ready. Pollinations token not configured; only public models are available.');
956+
}
957+
931958
try {
932959
setupRecognition();
933960
} catch (error) {
@@ -955,14 +982,15 @@ els.form.addEventListener('submit', async event => {
955982
if (!prompt) {
956983
throw new Error('Provide a prompt after /image');
957984
}
958-
const { dataUrl } = await generateImageAsset(prompt);
985+
const { dataUrl, seed } = await generateImageAsset(prompt);
959986
addMessage({
960987
role: 'assistant',
961988
type: 'image',
962989
url: dataUrl,
963990
alt: prompt,
964991
caption: prompt,
965992
});
993+
console.info('Generated Pollinations image with seed %s.', seed);
966994
resetStatusIfIdle();
967995
} else {
968996
await sendPrompt(raw);

0 commit comments

Comments
 (0)