mirror of
https://github.com/google/adk-docs.git
synced 2026-09-14 16:16:59 +08:00
50f7df7f46
* Add Kotlin to hero / front page * Add quickstart page for Kotlin * Complete Kotlin quickstart guide and fix hero code sample (#2) * Replace GitHub repo links with language icons in header (#3) * Fix header icon FOUC and homepage font weight regression (#4) * Testing staging pipeline * Revert test edit (for staging pipeline) * Update language icon tooltips to indicate GitHub destination (#5) * Add link to ADK Kotlin release notes (#7) * Initial commit of ADK Kotlin API reference docs (#6) * Add script to generate ADK Kotlin API reference docs (#8) * Update links and link checker ignore list (temporarily) (#9) * Add ADK Kotlin for Android getting started guide to Advanced setup page (#10) * Add advanced setup page with steps to "Use ADK Kotlin in Android projects" * Update temp link checker rules * Add placeholder folder for adk-samples (#13) * adding linter/compilation checks for kotlin snippets (#12) * adding linter/compilation checks for kotlin snippets * Add Kotlin validation scripts * Initial commit of Kotlin sample agents for adk-samples (#15) * Adding kotlin snippet for llm agents (#16) * Adding kotlin snippets to Events (#17) * Pull changes to docs/events/index.md from glaforge-kotlin-snippets * fixing kotlin event timestamp and longRunningToolIds * Fix language tags (#19) * Fix language tags * Update * Fix wrapping * Fix wrapping (again) * Fix wrapping/format * Fix language tag on integration page * Enable check_paths in PyMdown Snippets Extension to make the build fail if a snippet can't be found (#20) * Update mkdocs config (#21) * Fix broken links, update URLs to adk.dev, and improve (temp) lychee config (#22) * Add Kotlin/maven badge to README (#23) * Adding Kotlin snippets for artifacts (#18) * Pull Kotlin snippets for artifacts from glaforge-kotlin-snippets * Add comprehensive Kotlin snippets for artifacts * Refactor artifacts documentation to use external Kotlin snippets * Update Kotlin model to gemini-flash-latest * Fix GCS initialization in Kotlin artifact snippet * afixi failing test with capital-agent added to files_to_check * Fix snippet label syntax for MkDocs build * Configure proper Gradle project for Kotlin snippets and fix dependencies * Add KSP support and generated sources to Kotlin snippets build * fixing capital_agent turnComplete * Fix syntax error in build.gradle.kts by removing invalid placeholders (#25) * Adding Kotlin snippets to google-gemini.md (#27) Pulling kotlin changes to google-gemini.md from glaforge-kotlin-snippets * Add a warning about not adding an api key to production code. (#28) * Add a warning about not adding an api key to production code. * Update note --------- Co-authored-by: Kristopher Overholt <koverholt@google.com> * Add ADK Demo App sample showcasing Gemini-powered agents (#29) This sample demonstrates how to use the Google ADK (Agent Development Kit) in an Android application to create a chat interface powered by a Gemini-based "Fun Facts" agent. The implementation features: * Integration with the Kotlin ADK core and processor libraries. * A `FunFactsAgent` defined using `LlmAgent` and the Gemini model. * A `ChatViewModel` utilizing `InMemoryRunner` for asynchronous message streaming. * A modern UI built with Jetpack Compose and Material 3. * Build configuration logic for secure API key management via environment variables or `local.properties`. * Update Kotlin docs and samples to align with adk-kotlin API changes (#30) Rename GeminiModel to Gemini, @AdkTool/@AdkParam to @Tool/@Param, adkTools() to generatedTools(), replace DebugRunner with InMemoryRunner, fix AgentLoader import path, use SingleAgentLoader, bump Kotlin to 2.3.21 and KSP to 2.3.7, and update Android minSdk from 24 to 26. * adding kotlin info to READMEs (#14) * Reorganize Android sample agent and add READMEs (#31) * Move Android sample agent * Update repo README, add Android README, update sample agent README * Minor edit to language support tags (#32) * Remove blog post link (#33) Will re-add after it's published * Remove examples link (#34) * Adding Kotlin snippets for Sessions docs (#26) * initial kotlins snippets additions to sessions docs * Updating memory docs with kotlin snippets * Adding kotlin snippets to session state docs. * update model to gemini-flash-latest * sessions examples clean-up * fixing sessions snippet markers * adding kotlin session snippets to files to test * adding callback to memory_example * Fixing capital agent snippet (#35) Fixing file name Updating adkTool > Tool Updating GeminiModel > Gemini * Adding kotlin snippets for tools docs (#36) * adding function tool kotlin snippets * adding function_tools snippets to files to test * Adding kotlin snippets to observability docs (#37) * initial kotlin observability updates * adding observability snippets to file check (#38) * Adding Kotlin snippets to Callbacks docs (#39) * kotlin callbacks snippets * adding callbacks snippets to file check * Align Kotlin and KSP versions with published 0.1.0 artifacts (#40) * switch CLI entry points from InMemoryRunner to ReplRunner (#41) * Switch CLI entry points from InMemoryRunner to ReplRunner * Fix wording * Update API reference docs for Kotlin, 2026-05-18 (#42) * Remove ADK on Android note until published (#43) * Update Kotlin code samples (#44) * Rename GeminiModel to Gemini in Kotlin snippets and docs * Remove broken SessionKey call and use sessionId directly in AgentTool snippet * Rewrite Go hero snippet to use llmagent API * Use isFinalResponse with safe access in CapitalAgent snippet * Use Role.USER constant instead of raw string in SetupExample * Use full semver v0.1.0 in Kotlin language support tags * Remove Android setup steps, moving to new property (#45) * Tutorial Kotlin agent (#46) * Adding multi-tool-agent snippet and updating tutorial * Fixing Go language order on tutorial page * adding multi tool agent example to files to test * Inline Kotlin get-started code sample * Kotlin Multi agents snippets (#47) * Multi-agent kotlin snippets * Fixing docs tags in multiagent example * Fix Kotlin language support tags, code samples, and google-gemini.md cleanup (#48) * Add Kotlin v0.1.0 to language support tags across docs * Fix MultiToolAgent.kt model string and argument style * Update MultiAgentExample.kt to use gemini-flash-latest model string * Fix google-gemini.md: add Kotlin sample, remove unsupported Java tabs * Remove explicit apiKey from CallbackBasic.kt for consistency * Standardize Gemini() constructor to use named args in all snippets * Remove adk-samples directory (moved to google/adk-samples#1969) * Remove adk-samples directory (moved to google/adk-samples#1969) (#49) * Update API reference docs for ADK Kotlin 0.1.0 (#50) * Remove adk-samples directory (moved to google/adk-samples#1969) * Update API reference docs for ADK Kotlin 0.1.0 * Remove kotlin lycheeignore config (#51) * Remove adk-samples directory (moved to google/adk-samples#1969) * Remove Kotlin .lycheeignore config links --------- Co-authored-by: Toni Klopfenstein <2359976+ToniCorinne@users.noreply.github.com> Co-authored-by: Jolanda Verhoef <JolandaVerhoef@users.noreply.github.com>
401 lines
14 KiB
JavaScript
401 lines
14 KiB
JavaScript
/*
|
|
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
|
|
*/
|
|
|
|
filteringContext = {
|
|
dependencies: {},
|
|
restrictedDependencies: [],
|
|
activeFilters: []
|
|
}
|
|
let highlightedAnchor;
|
|
let topNavbarOffset;
|
|
let instances = [];
|
|
let sourcesetNotification;
|
|
|
|
const samplesDarkThemeName = 'darcula'
|
|
const samplesLightThemeName = 'idea'
|
|
|
|
window.addEventListener('load', () => {
|
|
document.querySelectorAll("div[data-platform-hinted]")
|
|
.forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event, elem)))
|
|
const filterSection = document.getElementById('filter-section')
|
|
if (filterSection) {
|
|
filterSection.addEventListener('click', (event) => filterButtonHandler(event))
|
|
initializeFiltering()
|
|
}
|
|
initTabs()
|
|
handleAnchor()
|
|
initHidingLeftNavigation()
|
|
topNavbarOffset = document.getElementById('navigation-wrapper')
|
|
darkModeSwitch()
|
|
})
|
|
|
|
const darkModeSwitch = () => {
|
|
const localStorageKey = "dokka-dark-mode"
|
|
const storage = localStorage.getItem(localStorageKey)
|
|
const osDarkSchemePreferred = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
const darkModeEnabled = storage ? JSON.parse(storage) : osDarkSchemePreferred
|
|
const element = document.getElementById("theme-toggle-button")
|
|
initPlayground(darkModeEnabled ? samplesDarkThemeName : samplesLightThemeName)
|
|
|
|
element.addEventListener('click', () => {
|
|
const enabledClasses = document.getElementsByTagName("html")[0].classList
|
|
enabledClasses.toggle("theme-dark")
|
|
|
|
//if previously we had saved dark theme then we set it to light as this is what we save in local storage
|
|
const darkModeEnabled = enabledClasses.contains("theme-dark")
|
|
if (darkModeEnabled) {
|
|
initPlayground(samplesDarkThemeName)
|
|
} else {
|
|
initPlayground(samplesLightThemeName)
|
|
}
|
|
localStorage.setItem(localStorageKey, JSON.stringify(darkModeEnabled))
|
|
})
|
|
}
|
|
|
|
const initPlayground = (theme) => {
|
|
if (!samplesAreEnabled()) return
|
|
instances.forEach(instance => instance.destroy())
|
|
instances = []
|
|
|
|
// Manually tag code fragments as not processed by playground since we also manually destroy all of its instances
|
|
document.querySelectorAll('code.runnablesample').forEach(node => {
|
|
node.removeAttribute("data-kotlin-playground-initialized");
|
|
})
|
|
|
|
KotlinPlayground('code.runnablesample', {
|
|
getInstance: playgroundInstance => {
|
|
instances.push(playgroundInstance)
|
|
},
|
|
theme: theme
|
|
});
|
|
}
|
|
|
|
// We check if type is accessible from the current scope to determine if samples script is present
|
|
// As an alternative we could extract this samples-specific script to new js file but then we would handle dark mode in 2 separate files which is not ideal
|
|
const samplesAreEnabled = () => {
|
|
try {
|
|
KotlinPlayground
|
|
return true
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
|
|
const initHidingLeftNavigation = () => {
|
|
document.getElementById("menu-toggle").onclick = function (event) {
|
|
//Events need to be prevented from bubbling since they will trigger next handler
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
event.stopImmediatePropagation();
|
|
document.getElementById("leftColumn").classList.toggle("open");
|
|
}
|
|
|
|
document.getElementById("main").onclick = () => {
|
|
document.getElementById("leftColumn").classList.remove("open");
|
|
}
|
|
}
|
|
|
|
// Hash change is needed in order to allow for linking inside the same page with anchors
|
|
// If this is not present user is forced to refresh the site in order to use an anchor
|
|
window.onhashchange = handleAnchor
|
|
|
|
function scrollToElementInContent(element) {
|
|
const scrollToElement = () => document.getElementById('main').scrollTo({
|
|
top: element.offsetTop - topNavbarOffset.offsetHeight,
|
|
behavior: "smooth"
|
|
})
|
|
|
|
const waitAndScroll = () => {
|
|
setTimeout(() => {
|
|
if (topNavbarOffset) {
|
|
scrollToElement()
|
|
} else {
|
|
waitForScroll()
|
|
}
|
|
}, 50)
|
|
}
|
|
|
|
if (topNavbarOffset) {
|
|
scrollToElement()
|
|
} else {
|
|
waitAndScroll()
|
|
}
|
|
}
|
|
|
|
|
|
function handleAnchor() {
|
|
if (highlightedAnchor) {
|
|
highlightedAnchor.classList.remove('anchor-highlight')
|
|
highlightedAnchor = null;
|
|
}
|
|
|
|
let searchForContentTarget = function (element) {
|
|
if (element && element.hasAttribute) {
|
|
if (element.hasAttribute("data-togglable")) return element.getAttribute("data-togglable");
|
|
else return searchForContentTarget(element.parentNode)
|
|
} else return null
|
|
}
|
|
|
|
let findAnyTab = function (target) {
|
|
let result = null
|
|
document.querySelectorAll('div[tabs-section] > button[data-togglable]')
|
|
.forEach(node => {
|
|
if(node.getAttribute("data-togglable").split(",").includes(target)) {
|
|
result = node
|
|
}
|
|
})
|
|
return result
|
|
}
|
|
|
|
let anchor = window.location.hash
|
|
if (anchor != "") {
|
|
anchor = anchor.substring(1)
|
|
let element = document.querySelector('a[data-name="' + anchor + '"]')
|
|
|
|
if (element) {
|
|
const content = element.nextElementSibling
|
|
const contentStyle = window.getComputedStyle(content)
|
|
if(contentStyle.display == 'none') {
|
|
let tab = findAnyTab(searchForContentTarget(content))
|
|
if (tab) {
|
|
toggleSections(tab)
|
|
}
|
|
}
|
|
|
|
if (content) {
|
|
content.classList.add('anchor-highlight')
|
|
highlightedAnchor = content
|
|
}
|
|
|
|
scrollToElementInContent(element)
|
|
}
|
|
}
|
|
}
|
|
|
|
function initTabs() {
|
|
// we could have only a single type of data - classlike or package
|
|
const mainContent = document.querySelector('.main-content');
|
|
const type = mainContent ? mainContent.getAttribute("data-page-type") : null;
|
|
const localStorageKey = "active-tab-" + type;
|
|
document.querySelectorAll('div[tabs-section]').forEach(element => {
|
|
showCorrespondingTabBody(element);
|
|
element.addEventListener('click', ({target}) => {
|
|
const togglable = target ? target.getAttribute("data-togglable") : null;
|
|
if (!togglable) return;
|
|
|
|
localStorage.setItem(localStorageKey, JSON.stringify(togglable));
|
|
toggleSections(target);
|
|
});
|
|
});
|
|
|
|
const cached = localStorage.getItem(localStorageKey);
|
|
if (!cached) return;
|
|
|
|
const tab = document.querySelector(
|
|
'div[tabs-section] > button[data-togglable="' + JSON.parse(cached) + '"]'
|
|
);
|
|
if (!tab) return;
|
|
|
|
toggleSections(tab);
|
|
}
|
|
|
|
function showCorrespondingTabBody(element) {
|
|
const buttonWithKey = element.querySelector("button[data-active]")
|
|
if (buttonWithKey) {
|
|
toggleSections(buttonWithKey)
|
|
}
|
|
}
|
|
|
|
function filterButtonHandler(event) {
|
|
if (event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) {
|
|
let sourceset = event.target.getAttribute("data-filter")
|
|
if (filteringContext.activeFilters.indexOf(sourceset) != -1) {
|
|
filterSourceset(sourceset)
|
|
} else {
|
|
unfilterSourceset(sourceset)
|
|
}
|
|
}
|
|
}
|
|
|
|
function initializeFiltering() {
|
|
filteringContext.dependencies = JSON.parse(sourceset_dependencies)
|
|
document.querySelectorAll("#filter-section > button")
|
|
.forEach(p => filteringContext.restrictedDependencies.push(p.getAttribute("data-filter")))
|
|
Object.keys(filteringContext.dependencies).forEach(p => {
|
|
filteringContext.dependencies[p] = filteringContext.dependencies[p]
|
|
.filter(q => -1 !== filteringContext.restrictedDependencies.indexOf(q))
|
|
})
|
|
let cached = window.localStorage.getItem('inactive-filters')
|
|
if (cached) {
|
|
let parsed = JSON.parse(cached)
|
|
filteringContext.activeFilters = filteringContext.restrictedDependencies
|
|
.filter(q => parsed.indexOf(q) == -1)
|
|
} else {
|
|
filteringContext.activeFilters = filteringContext.restrictedDependencies
|
|
}
|
|
refreshFiltering()
|
|
}
|
|
|
|
function filterSourceset(sourceset) {
|
|
filteringContext.activeFilters = filteringContext.activeFilters.filter(p => p != sourceset)
|
|
refreshFiltering()
|
|
addSourcesetFilterToCache(sourceset)
|
|
}
|
|
|
|
function unfilterSourceset(sourceset) {
|
|
if (filteringContext.activeFilters.length == 0) {
|
|
filteringContext.activeFilters = filteringContext.dependencies[sourceset].concat([sourceset])
|
|
refreshFiltering()
|
|
filteringContext.dependencies[sourceset].concat([sourceset]).forEach(p => removeSourcesetFilterFromCache(p))
|
|
} else {
|
|
filteringContext.activeFilters.push(sourceset)
|
|
refreshFiltering()
|
|
removeSourcesetFilterFromCache(sourceset)
|
|
}
|
|
|
|
}
|
|
|
|
function addSourcesetFilterToCache(sourceset) {
|
|
let cached = localStorage.getItem('inactive-filters')
|
|
if (cached) {
|
|
let parsed = JSON.parse(cached)
|
|
localStorage.setItem('inactive-filters', JSON.stringify(parsed.concat([sourceset])))
|
|
} else {
|
|
localStorage.setItem('inactive-filters', JSON.stringify([sourceset]))
|
|
}
|
|
}
|
|
|
|
function removeSourcesetFilterFromCache(sourceset) {
|
|
let cached = localStorage.getItem('inactive-filters')
|
|
if (cached) {
|
|
let parsed = JSON.parse(cached)
|
|
localStorage.setItem('inactive-filters', JSON.stringify(parsed.filter(p => p != sourceset)))
|
|
}
|
|
}
|
|
|
|
function toggleSections(target) {
|
|
const activateTabs = (containerClass) => {
|
|
for (const element of document.getElementsByClassName(containerClass)) {
|
|
for (const child of element.children) {
|
|
if (child.getAttribute("data-togglable") === target.getAttribute("data-togglable")) {
|
|
child.setAttribute("data-active", "")
|
|
} else {
|
|
child.removeAttribute("data-active")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const toggleTargets = target.getAttribute("data-togglable").split(",")
|
|
const activateTabsBody = (containerClass) => {
|
|
document.querySelectorAll("." + containerClass + " *[data-togglable]")
|
|
.forEach(child => {
|
|
if (toggleTargets.includes(child.getAttribute("data-togglable"))) {
|
|
child.setAttribute("data-active", "")
|
|
} else if(!child.classList.contains("sourceset-dependent-content")) { // data-togglable is used to switch source set as well, ignore it
|
|
child.removeAttribute("data-active")
|
|
}
|
|
})
|
|
}
|
|
activateTabs("tabs-section")
|
|
activateTabsBody("tabs-section-body")
|
|
}
|
|
|
|
function togglePlatformDependent(e, container) {
|
|
let target = e.target
|
|
if (target.tagName != 'BUTTON') return;
|
|
let index = target.getAttribute('data-toggle')
|
|
|
|
for (let child of container.children) {
|
|
if (child.hasAttribute('data-toggle-list')) {
|
|
for (let bm of child.children) {
|
|
if (bm == target) {
|
|
bm.setAttribute('data-active', "")
|
|
} else if (bm != target) {
|
|
bm.removeAttribute('data-active')
|
|
}
|
|
}
|
|
} else if (child.getAttribute('data-togglable') == index) {
|
|
child.setAttribute('data-active', "")
|
|
} else {
|
|
child.removeAttribute('data-active')
|
|
}
|
|
}
|
|
}
|
|
|
|
function refreshFiltering() {
|
|
let sourcesetList = filteringContext.activeFilters
|
|
document.querySelectorAll("[data-filterable-set]")
|
|
.forEach(
|
|
elem => {
|
|
let platformList = elem.getAttribute("data-filterable-set").split(',').filter(v => -1 !== sourcesetList.indexOf(v))
|
|
elem.setAttribute("data-filterable-current", platformList.join(','))
|
|
}
|
|
)
|
|
refreshFilterButtons()
|
|
refreshPlatformTabs()
|
|
refreshNoContentNotification()
|
|
refreshPlaygroundSamples()
|
|
}
|
|
|
|
function refreshPlaygroundSamples() {
|
|
document.querySelectorAll('code.runnablesample').forEach(node => {
|
|
const playground = node.KotlinPlayground;
|
|
/* Some samples may be hidden by filter, they have 0px height for visible code area
|
|
* after rendering. Call this method for re-calculate code area height */
|
|
playground && playground.view.codemirror.refresh();
|
|
});
|
|
}
|
|
|
|
function refreshNoContentNotification() {
|
|
const element = document.getElementsByClassName("main-content")[0]
|
|
if(filteringContext.activeFilters.length === 0){
|
|
element.style.display = "none";
|
|
|
|
const appended = document.createElement("div")
|
|
appended.className = "filtered-message"
|
|
appended.innerText = "All documentation is filtered, please adjust your source set filters in top-right corner of the screen"
|
|
sourcesetNotification = appended
|
|
element.parentNode.prepend(appended)
|
|
} else {
|
|
if(sourcesetNotification) sourcesetNotification.remove()
|
|
element.style.display = "block"
|
|
}
|
|
}
|
|
|
|
function refreshPlatformTabs() {
|
|
document.querySelectorAll(".platform-hinted > .platform-bookmarks-row").forEach(
|
|
p => {
|
|
let active = false;
|
|
let firstAvailable = null
|
|
p.childNodes.forEach(
|
|
element => {
|
|
if (element.getAttribute("data-filterable-current") != '') {
|
|
if (firstAvailable == null) {
|
|
firstAvailable = element
|
|
}
|
|
if (element.hasAttribute("data-active")) {
|
|
active = true;
|
|
}
|
|
}
|
|
}
|
|
)
|
|
if (active == false && firstAvailable) {
|
|
firstAvailable.click()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
function refreshFilterButtons() {
|
|
document.querySelectorAll("#filter-section > button")
|
|
.forEach(f => {
|
|
if (filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1) {
|
|
f.setAttribute("data-active", "")
|
|
} else {
|
|
f.removeAttribute("data-active")
|
|
}
|
|
})
|
|
}
|