forked from elizaOS/eliza
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from storyprotocol/allen/add-story-apis
Add Story API scaffold - getAllLicenses
- Loading branch information
Showing
10 changed files
with
1,374 additions
and
329 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
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 |
---|---|---|
|
@@ -18,5 +18,8 @@ | |
}, | ||
"peerDependencies": { | ||
"whatwg-url": "7.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^22.10.1" | ||
} | ||
} |
170 changes: 170 additions & 0 deletions
170
packages/plugin-story/src/actions/getAvailableLicenses.ts
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,170 @@ | ||
import { | ||
composeContext, | ||
elizaLogger, | ||
generateObjectDEPRECATED, | ||
HandlerCallback, | ||
ModelClass, | ||
type IAgentRuntime, | ||
type Memory, | ||
type State, | ||
} from "@ai16z/eliza"; | ||
import { getAvailableLicensesTemplate, licenseIPTemplate } from "../templates"; | ||
import { Address } from "viem"; | ||
import { IPLicenseDetails, RESOURCE_TYPE } from "../types/api"; | ||
import { API_KEY, API_URL } from "../lib/api"; | ||
import { storyOdyssey } from "viem/chains"; | ||
|
||
export { licenseIPTemplate }; | ||
|
||
type GetAvailableLicensesParams = { | ||
ipid: Address; | ||
}; | ||
|
||
type GetAvailableLicensesResponse = { | ||
data: IPLicenseDetails[]; | ||
}; | ||
|
||
export class GetAvailableLicensesAction { | ||
constructor() {} | ||
|
||
async getAvailableLicenses( | ||
params: GetAvailableLicensesParams | ||
): Promise<GetAvailableLicensesResponse> { | ||
const ipLicenseTermsQueryOptions = { | ||
pagination: { | ||
limit: 10, | ||
offset: 0, | ||
}, | ||
orderBy: "blockNumber", | ||
orderDirection: "desc", | ||
}; | ||
|
||
elizaLogger.log( | ||
"Fetching from", | ||
`${API_URL}/${RESOURCE_TYPE.IP_LICENSE_DETAILS}` | ||
); | ||
const response = await fetch( | ||
`${API_URL}/${RESOURCE_TYPE.IP_LICENSE_DETAILS}`, | ||
{ | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
"x-api-key": API_KEY, | ||
"x-chain": storyOdyssey.id.toString(), | ||
}, | ||
cache: "no-cache", | ||
body: JSON.stringify({ | ||
ip_ids: [params.ipid], // Use the provided IPID instead of hardcoded value | ||
options: ipLicenseTermsQueryOptions, // Use the defined query options | ||
}), | ||
} | ||
); | ||
|
||
if (!response.ok) { | ||
throw new Error(`HTTP error! status: ${response.status}`); | ||
} | ||
|
||
const text = await response.text(); | ||
try { | ||
const licenseDetailsResponse = JSON.parse(text); | ||
elizaLogger.log("licenseDetailsResponse", licenseDetailsResponse); | ||
return licenseDetailsResponse; | ||
} catch (e) { | ||
elizaLogger.error("Failed to parse response:", text); | ||
throw new Error(`Failed to parse JSON response: ${e.message}`); | ||
} | ||
} | ||
} | ||
|
||
export const getAvailableLicensesAction = { | ||
name: "GET_AVAILABLE_LICENSES", | ||
description: "Get available licenses for an IP Asset on Story", | ||
handler: async ( | ||
runtime: IAgentRuntime, | ||
message: Memory, | ||
state: State, | ||
options: any, | ||
callback?: HandlerCallback | ||
): Promise<boolean> => { | ||
elizaLogger.log("Starting GET_AVAILABLE_LICENSES handler..."); | ||
|
||
// initialize or update state | ||
if (!state) { | ||
state = (await runtime.composeState(message)) as State; | ||
} else { | ||
state = await runtime.updateRecentMessageState(state); | ||
} | ||
|
||
const getAvailableLicensesContext = composeContext({ | ||
state, | ||
template: getAvailableLicensesTemplate, | ||
}); | ||
|
||
const content = await generateObjectDEPRECATED({ | ||
runtime, | ||
context: getAvailableLicensesContext, | ||
modelClass: ModelClass.SMALL, | ||
}); | ||
|
||
const action = new GetAvailableLicensesAction(); | ||
try { | ||
const response = await action.getAvailableLicenses(content); | ||
|
||
// TODO: need to format this better into human understandable terms | ||
const formattedResponse = response.data | ||
.map((license) => { | ||
const terms = license.terms; | ||
return `License ID: ${license.id} | ||
- Terms: | ||
• Commercial Use: ${terms.commercialUse ? "Allowed" : "Not Allowed"} | ||
• Commercial Attribution: ${terms.commercialAttribution ? "Required" : "Not Required"} | ||
• Derivatives: ${terms.derivativesAllowed ? "Allowed" : "Not Allowed"} | ||
• Derivatives Attribution: ${terms.derivativesAttribution ? "Required" : "Not Required"} | ||
• Derivatives Approval: ${terms.derivativesApproval ? "Required" : "Not Required"} | ||
• Revenue Share: ${terms.commercialRevenueShare ? terms.commercialRevenueShare + "%" : "Not Required"} | ||
`; | ||
}) | ||
.join("\n"); | ||
|
||
callback?.({ | ||
text: formattedResponse, | ||
action: "GET_AVAILABLE_LICENSES", | ||
source: "Story Protocol API", | ||
}); | ||
return true; | ||
} catch (e) { | ||
elizaLogger.error("Error fetching available licenses:", e.message); | ||
callback?.({ | ||
text: `Error fetching available licenses: ${e.message}`, | ||
}); | ||
return false; | ||
} | ||
}, | ||
template: getAvailableLicensesTemplate, | ||
validate: async (runtime: IAgentRuntime) => { | ||
return true; | ||
}, | ||
examples: [ | ||
[ | ||
{ | ||
user: "assistant", | ||
content: { | ||
text: "Getting available licenses for an IP Asset 0x2265F2b8e47F98b3Bdf7a1937EAc27282954A4Db", | ||
action: "GET_AVAILABLE_LICENSES", | ||
}, | ||
}, | ||
{ | ||
user: "user", | ||
content: { | ||
text: "Get available licenses for an IP Asset 0x2265F2b8e47F98b3Bdf7a1937EAc27282954A4Db", | ||
action: "GET_AVAILABLE_LICENSES", | ||
}, | ||
}, | ||
], | ||
], | ||
similes: [ | ||
"AVAILABLE_LICENSES", | ||
"AVAILABLE_LICENSES_FOR_IP", | ||
"AVAILABLE_LICENSES_FOR_ASSET", | ||
], | ||
}; |
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,116 @@ | ||
import { | ||
IPLicenseTerms, | ||
PILTerms, | ||
QUERY_ORDER_BY, | ||
QUERY_ORDER_DIRECTION, | ||
QueryOptions, | ||
RESOURCE_TYPE, | ||
ResourceType, | ||
Trait, | ||
} from "../types/api"; | ||
import { elizaLogger } from "@ai16z/eliza"; | ||
|
||
import { camelize } from "./utils"; | ||
const API_BASE_URL = process.env.STORY_API_BASE_URL; | ||
const API_VERSION = "v2"; | ||
export const API_URL = `${API_BASE_URL}/${API_VERSION}`; | ||
export const API_KEY = process.env.STORY_API_KEY || ""; | ||
|
||
export async function getResource( | ||
resourceName: ResourceType, | ||
resourceId: string, | ||
options?: QueryOptions | ||
) { | ||
try { | ||
const res = await fetch(`${API_URL}/${resourceName}/${resourceId}`, { | ||
method: "GET", | ||
headers: { | ||
"Content-Type": "application/json", | ||
"x-api-key": API_KEY as string, | ||
"x-chain": "1516", | ||
}, | ||
}); | ||
if (res.ok) { | ||
return res.json(); | ||
} | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
} | ||
|
||
export async function listResource( | ||
resourceName: ResourceType, | ||
options?: QueryOptions | ||
) { | ||
try { | ||
const _options = { | ||
pagination: { | ||
limit: 10, | ||
offset: 0, | ||
}, | ||
orderBy: QUERY_ORDER_BY.BLOCK_NUMBER, | ||
orderDirection: QUERY_ORDER_DIRECTION.DESC, | ||
...options, | ||
}; | ||
elizaLogger.log(`Calling Story API ${resourceName}`); | ||
elizaLogger.log(`STORY_API_KEY: ${API_KEY}`); | ||
elizaLogger.log(`API_URL: ${API_URL}`); | ||
elizaLogger.log(`API_VERSION: ${API_VERSION}`); | ||
elizaLogger.log(`_options: ${JSON.stringify(_options)}`); | ||
const res = await fetch(`${API_URL}/${resourceName}`, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
"x-api-key": API_KEY as string, | ||
"x-chain": "1516", | ||
}, | ||
cache: "no-cache", | ||
...(_options && { body: JSON.stringify({ options: _options }) }), | ||
}); | ||
if (res.ok) { | ||
elizaLogger.log("Response is ok"); | ||
elizaLogger.log(res.ok); | ||
return res.json(); | ||
} else { | ||
elizaLogger.log("Response is not ok"); | ||
elizaLogger.log(res); | ||
return res; | ||
} | ||
} catch (error) { | ||
elizaLogger.log("List resource Error"); | ||
console.error(error); | ||
} | ||
} | ||
|
||
export async function fetchLicenseTermsDetails(data: IPLicenseTerms[]) { | ||
const requests = data.map((item) => | ||
getResource(RESOURCE_TYPE.LICENSE_TERMS, item.licenseTermsId) | ||
); | ||
const results = await Promise.all(requests); | ||
|
||
return results | ||
.filter((value) => !!value) | ||
.map((result) => { | ||
return { | ||
...result.data, | ||
licenseTerms: convertLicenseTermObject( | ||
result.data.licenseTerms | ||
), | ||
}; | ||
}); | ||
} | ||
|
||
type LicenseTerms = Partial<PILTerms>; | ||
|
||
export function convertLicenseTermObject(licenseTerms: Trait[]): LicenseTerms { | ||
return licenseTerms.reduce((acc, option: Trait): LicenseTerms => { | ||
const key = camelize(option.trait_type) as keyof PILTerms; | ||
acc[key] = | ||
option.value === "true" | ||
? true | ||
: option.value === "false" | ||
? false | ||
: (option.value as any); | ||
return acc as LicenseTerms; | ||
}, {}); | ||
} |
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,6 @@ | ||
export function camelize(str: string) { | ||
return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index) { | ||
if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces | ||
return index === 0 ? match.toLowerCase() : match.toUpperCase(); | ||
}); | ||
} |
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.