-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a8af0ae
commit 07ab435
Showing
15 changed files
with
546 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { APIError, APIHandler } from './helpers/endpoint' | ||
import { largePerplexityModel, perplexity } from 'shared/helpers/perplexity' | ||
import { models, promptClaude } from 'shared/helpers/claude' | ||
import { AIGeneratedMarket } from 'common/contract' | ||
import { log } from 'shared/utils' | ||
import { | ||
claudeSystemPrompt, | ||
guidelinesPrompt, | ||
} from 'common/ai-creation-prompts' | ||
import { anythingToRichText } from 'shared/tiptap' | ||
import { track } from 'shared/analytics' | ||
|
||
export const generateAIMarketSuggestions: APIHandler< | ||
'generate-ai-market-suggestions' | ||
> = async (props, auth) => { | ||
const { prompt } = props | ||
// TODO: if the prompt is a url, either fetch the content or use gpt4 to summarize it | ||
|
||
const perplexityResponse = await perplexity(`${prompt}`, { | ||
model: largePerplexityModel, | ||
}) | ||
const { messages, citations } = perplexityResponse | ||
log('Perplexity response:', messages.join('\n')) | ||
log('Sources:', citations.join('\n')) | ||
// Format the perplexity suggestions for Claude | ||
const claudePrompt = ` | ||
Convert these prediction market ideas into valid JSON objects that abide by the following Manifold Market schema. Each object should include: | ||
- question (string with 120 characters or less, required) | ||
- Question should be worded as a statement, i.e. Stock price of Tesla above $420 by x date, not Will the stock price of Tesla be above $420 by x date? | ||
- descriptionMarkdown (markdown string, required) | ||
- The description should be a concise summary of the market's context, possible outcomes, sources, and resolution criteria. | ||
- closeDate (string, date in YYYY-MM-DD format, required) | ||
- The close date is when trading stops for the market, and resolution can be made. E.g. if the title includes 'by january 1st 2025', the close date should be 2025-12-31 | ||
- outcomeType ("BINARY", "INDEPENDENT_MULTIPLE_CHOICE", "DEPENDENT_MULTIPLE_CHOICE", "POLL", required) | ||
- "BINARY" means there are only two answers, true (yes) or false (no) | ||
- "INDEPENDENT_MULTIPLE_CHOICE" means there are multiple answers, but they are independent of each other (e.g. What will happen during the next presidential debate?) | ||
- "DEPENDENT_MULTIPLE_CHOICE" means there are multiple answers, but they are dependent on each other (e.g. Who will win the presidential election?) | ||
- "POLL" means the question is about a personal matter, i.e. "Should I move to a new city?", "Should I get a new job?", etc. | ||
- answers (array of strings, recommended only if outcomeType is one of the "MULTIPLE_CHOICE" types) | ||
- addAnswersMode ("DISABLED", "ONLY_CREATOR", or "ANYONE", required if one of the "MULTIPLE_CHOICE" types is provided) | ||
- "DISABLED" means that the answers list covers all possible outcomes and no more answers can be added after the market is created | ||
- "ONLY_CREATOR" means that only the creator can add answers after the market is created | ||
- "ANYONE" means that anyone can add answers after the market is created | ||
- reasoning (string, required - extract the reasoning section from each market suggestion) | ||
Please review the market suggestions and refine them according to the following guidelines: | ||
${guidelinesPrompt} | ||
Here is the original user's prompt: | ||
${prompt} | ||
Here are the market suggestions to refine and convert into valid JSON objects: | ||
${messages.join('\n')} | ||
${ | ||
citations.length > 0 | ||
? `Find any references to sources in the market suggestions, (indicated by **Source:**[#], or [#], or **Source:** [Name][#], or [1], [2], etc.), and use the following References section to append them at the end of each description. Format them with a references header and a bulleted list. | ||
References: | ||
${citations.join('\n')}` | ||
: '' | ||
} | ||
ONLY return a valid JSON array of market objects and do NOT include any other text. | ||
` | ||
|
||
const claudeResponse = await promptClaude(claudePrompt, { | ||
model: models.sonnet, | ||
system: claudeSystemPrompt, | ||
}) | ||
|
||
// Parse the JSON response | ||
let parsedMarkets: AIGeneratedMarket[] = [] | ||
try { | ||
parsedMarkets = JSON.parse(claudeResponse).map( | ||
(market: AIGeneratedMarket) => ({ | ||
...market, | ||
description: anythingToRichText({ | ||
markdown: market.descriptionMarkdown, | ||
}), | ||
}) | ||
) | ||
} catch (e) { | ||
console.error('Failed to parse Claude response:', e) | ||
throw new APIError( | ||
500, | ||
'Failed to parse market suggestions from Claude. Please try again.' | ||
) | ||
} | ||
track(auth.uid, 'generate-ai-market-suggestions', { | ||
marketTitles: parsedMarkets.map((m) => m.question), | ||
prompt, | ||
}) | ||
|
||
return parsedMarkets | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { APIError } from 'common/api/utils' | ||
import { | ||
guidelinesPrompt, | ||
perplexitySystemPrompt, | ||
} from 'common/ai-creation-prompts' | ||
export const smallPerplexityModel = 'llama-3.1-sonar-small-128k-online' | ||
export const largePerplexityModel = 'llama-3.1-sonar-large-128k-online' | ||
export const hugePerplexityModel = 'llama-3.1-sonar-huge-128k-online' | ||
|
||
export const perplexity = async ( | ||
query: string, | ||
options: { model?: string } = {} | ||
) => { | ||
const apiKey = process.env.PERPLEXITY_API_KEY | ||
const { model = smallPerplexityModel } = options | ||
const requestOptions = { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `Bearer ${apiKey}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
model, | ||
messages: [ | ||
{ | ||
role: 'system', | ||
content: perplexitySystemPrompt, | ||
}, | ||
{ | ||
role: 'system', | ||
content: guidelinesPrompt, | ||
}, | ||
{ role: 'user', content: query }, | ||
], | ||
temperature: 0.2, | ||
return_citations: true, | ||
}), | ||
} | ||
|
||
try { | ||
const response = await fetch( | ||
'https://api.perplexity.ai/chat/completions', | ||
requestOptions | ||
) | ||
const data = await response.json() | ||
|
||
// Extract citations if they exist | ||
const citations = data.citations || [] | ||
|
||
// Map the choices and attach only referenced citations | ||
const messages = data.choices.map( | ||
(choice: any) => choice.message.content | ||
) as string[] | ||
|
||
return { messages, citations } | ||
} catch (err) { | ||
console.error(err) | ||
throw new APIError(500, 'Failed to generate markets') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { MONTH_MS } from './util/time' | ||
|
||
export const guidelinesPrompt = ` | ||
When designing market suggestions for the user, follow these guidelines: | ||
EVIDENCE-BASED | ||
- Markets must be directly related to user's interest and sources about the topic | ||
- Focus on natural next steps or developments based on information from sources | ||
- Avoid highly unlikely scenarios | ||
QUICK RESOLUTION | ||
- At least half of the markets should resolve no later than ${new Date( | ||
Date.now() + 3 * MONTH_MS | ||
).toLocaleDateString('en-US', { | ||
year: 'numeric', | ||
month: 'long', | ||
day: 'numeric', | ||
})}. All of them should resolve no later than ${new Date( | ||
Date.now() + 6 * MONTH_MS | ||
).toLocaleDateString('en-US', { | ||
year: 'numeric', | ||
month: 'long', | ||
day: 'numeric', | ||
})} unless otherwise specified by the user | ||
- Prioritize markets that resolve sooner, using leading indicators when possible, ie stock performance of a company upon earliest possible news release, etc. | ||
- Include markets with known, upcoming resolution dates, i.e. elections, sports events, sentencing/court dates, etc. | ||
- Be sure to include 'before [date]' or 'on [event]' in the title | ||
CLEAR RESOLUTION CRITERIA | ||
- Unambiguous outcomes that can be definitively verified | ||
- Specific resolution criteria and trusted source(s) | ||
- Avoid subjective or feeling-based outcomes unless prompted by the user | ||
- Prefer events that either resolve YES or NO, and avoid situations that resolve N/A (cancels all trades because a precondition is not met) | ||
MARKETS INVITING DISAGREEMENT & UNCERTAINTY | ||
- Aim for markets where reasonable people might disagree | ||
- Avoid markets where one outcome is overwhelmingly likely | ||
MULTIPLE CHOICE MARKETS | ||
- If a market is about a number, range of dates, or other options, include an answers array that includes reasonable options based on the news article/surrounding context. | ||
- I.e. for a question like "How many US states will face historic drought conditions by December 31?", include an answers array that seems reasonable from the context. | ||
PERSONAL QUESTIONS | ||
- It's possible the user is looking for advice, i.e. their prompt includes the word "shold" in it, like "Should I move to a new city?", "Should I get a new job?", etc., the outcome should be "POLL" | ||
- "POLL" outcomes require an answers array for users to vote on, and resolve automatically on the close date based on votes, rather than trades. | ||
- If a question is about events closely related to the user, the question and description should be worded from the creator's point of view, i.e. 'I move to Colorado by August 1st 2025', 'I get a new job by December 31st 2025', etc. | ||
- Personal questions may still be markets (i.e. non-POLL outcomes) if they have clear resolution criteria and are not based on opinions. | ||
Following each market suggestion, add a "Reasoning:" section that addresses the following points: | ||
1. A clear explanation of why this market follows from the source material | ||
2. Why it's a good prediction market (e.g., has clear resolution criteria, neither a yes nor no outcome is overwhelmingly likely, etc. from above) | ||
In recap: | ||
- Focus on SHORT-TERM markets that resolve within days, weeks, or a few months. | ||
- Only create markets based on CONCRETE EVIDENCE and LIKELY OUTCOMES from the source material. | ||
- Avoid speculative leaps or assumptions not supported by the content. | ||
` | ||
|
||
export const perplexitySystemPrompt = `You are a helpful assistant that creates engaging prediction markets on Manifold Markets. | ||
Your role is to transform a user's prompt into at least approximately 6 well-structured prediction markets that encourage participation and meaningful forecasting. | ||
` | ||
export const claudeSystemPrompt = `You are a helpful assistant that refines and converts market ideas into valid JSON objects following Manifold's schema. ONLY return a valid JSON array of market objects.` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.