Initial MKDocs (#285)

This commit is contained in:
William FH
2024-04-08 13:40:21 -07:00
committed by GitHub
parent a3c4d4bdd1
commit 2e9e3b0a6c
16 changed files with 732 additions and 2 deletions
+52
View File
@@ -0,0 +1,52 @@
name: Deploy SDK Docs
on:
push:
branches:
- master
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: install deps
run: |
pip install poetry poethepoet
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.12
cache: poetry
cache-dependency-path: 'poetry.lock'
- name: Poetry install
run: |
poetry install
poetry run pip install -r docs/docs-requirements.txt
- name: Build site
working-directory: ./docs
run: make build-docs
- name: Configure GitHub Pages
uses: actions/configure-pages@v4
- name: Upload Pages Artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./docs/site/
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+4 -1
View File
@@ -22,6 +22,9 @@ jobs:
uses: actions/checkout@v4
- name: Check links in Markdown files
uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
folder-path: 'examples/'
file-path: './README.md'
notebook-link-check:
runs-on: ubuntu-latest
@@ -45,4 +48,4 @@ jobs:
env:
LANGCHAIN_API_KEY: test
shell: bash
run: poetry run pytest -o python_files=non_python_only --check-links --ignore="*.py" -k .ipynb --check-links-ignore "https://(api|web)\.smith\.langchain\.com/.*" --check-links-ignore "https://x.com/.*" .
run: poetry run pytest -o python_files=non_python_only --check-links --ignore="*.py" -k .ipynb --check-links-ignore "https://(api|web)\.smith\.langchain\.com/.*" --check-links-ignore "https://x.com/.*" ./examples
+8 -1
View File
@@ -1,4 +1,4 @@
.PHONY: all clean docs_build docs_clean docs_linkcheck api_docs_build api_docs_clean api_docs_linkcheck format lint test tests test_watch integration_tests docker_tests help extended_tests
.PHONY: all clean docs_build docs_clean docs_linkcheck api_docs_build api_docs_clean api_docs_linkcheck format lint test tests test_watch integration_tests docker_tests help extended_tests coverage spell_check spell_fix build-docs serve-docs
# Default target executed when no arguments are given to make.
all: help
@@ -49,6 +49,13 @@ spell_check:
spell_fix:
poetry run codespell --toml pyproject.toml -w
build-docs:
poetry run python docs/_scripts/copy_notebooks.py
poetry run mkdocs build --clean -f docs/mkdocs.yml
serve-docs: build-docs
poetry run mkdocs serve -f docs/mkdocs.yml
######################
# HELP
######################
+2
View File
@@ -0,0 +1,2 @@
*.ipynb
site/
+64
View File
@@ -0,0 +1,64 @@
import os
import shutil
from pathlib import Path
root_dir = Path(__file__).resolve().parents[2]
examples_dir = root_dir / "examples"
docs_dir = root_dir / "docs/docs"
how_tos_dir = docs_dir / "how-tos"
tutorials_dir = docs_dir / "tutorials"
_HOW_TOS = {"agent_executor", "chat_agent_executor_with_function_calling", "docs"}
_IGNORE = (".ipynb_checkpoints", ".venv", ".cache")
def clean_notebooks():
roots = (how_tos_dir, tutorials_dir)
for dir_ in roots:
traversed = []
for root, dirs, files in os.walk(dir_):
for file in files:
if file.endswith(".ipynb"):
os.remove(os.path.join(root, file))
# Now delete the dir if it is empty now
if root not in roots:
traversed.append(root)
for root in reversed(traversed):
if not os.listdir(root):
os.rmdir(root)
def copy_notebooks():
# Nested ones are mostly tutorials rn
for root, dirs, files in os.walk(examples_dir):
if root == str(examples_dir):
continue
if any(path.startswith(".") or path.startswith("__") for path in root.split(os.sep)):
continue
if any(path in _HOW_TOS for path in root.split(os.sep)):
dst_dir = how_tos_dir
else:
dst_dir = tutorials_dir
for file in files:
if file.endswith(".ipynb"):
src_path = os.path.join(root, file)
dst_path = os.path.join(
dst_dir, os.path.relpath(src_path, examples_dir)
)
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
shutil.copy(src_path, dst_path)
# Top level notebooks are "how-to's"
for file in examples_dir.iterdir():
if file.suffix.endswith(".ipynb") and not os.path.isdir(
os.path.join(examples_dir, file)
):
src_path = os.path.join(examples_dir, file)
dst_path = os.path.join(docs_dir, "how-tos", file.name)
shutil.copy(src_path, dst_path)
if __name__ == "__main__":
clean_notebooks()
copy_notebooks()
+7
View File
@@ -0,0 +1,7 @@
/* https://mkdocstrings.github.io/crystal/styling.html#recommended-styles */
/* Indent and distinguish sub-items */
div.doc-contents:not(.first) {
padding-left: 15px;
border-left: 4px solid rgba(230, 230, 230);
}
+8
View File
@@ -0,0 +1,8 @@
mkdocs
mkdocstrings
mkdocstrings-python
mkdocs-jupyter
mkdocs-redirects
mkdocs-minify-plugin
mkdocs-rss-plugin
mkdocs-material[imaging]
+4
View File
@@ -0,0 +1,4 @@
# Concepts
## State
+25
View File
@@ -0,0 +1,25 @@
# How-To Guides
Welcome to the LangGraph How-To Guides! These guides provide practical, step-by-step instructions for accomplishing key tasks in LangGraph.
## Basics
- [State Management](state-model.ipynb): How to define and manage complex state in your graphs
- [Tool Integration](sql_example.ipynb): How to integrate external tools and data sources
- [Human-in-the-Loop](human-in-the-loop.ipynb): How to incorporate human feedback and intervention
## Performance
- [Async Execution](async.ipynb): How to run nodes asynchronously for improved performance
- [Streaming Responses](streaming-tokens.ipynb): How to stream agent responses in real-time
## Graph Structure
- [Subgraphs](subgraph.ipynb): How to modularize your graphs with subgraphs
- [Branching](branching.ipynb): How to create branching logic in your graphs
## Development
- [Persistence](persistence.ipynb): How to save and load graph state for long-running applications
- [Visualization](visualization.ipynb): How to visualize your graphs
- [Time Travel](time-travel.ipynb): How to navigate and manipulate graph execution history
+122
View File
@@ -0,0 +1,122 @@
# 🦜🕸️LangGraph
[![Downloads](https://static.pepy.tech/badge/langgraph/month)](https://pepy.tech/project/langgraph)
⚡ Build language agents as graphs ⚡
## Overview
Suppose you're building a customer support assistant. You want your assistant to:
1. Try to answer user questions using a knowledge base
2. Escalate to a human if it's not confident in its answer
3. Relay the human's resolution back to the user
4. Remember the full conversation context across multiple user messages
With raw LLMs, the code to control the agentic loop, conversation state, route between the chatbot and human, and checkpoint the full application state can get complex.
LangGraph makes it simple. First install:
```shell
pip install -U langgraph
```
Then define your assistant:
```python
from langgraph.graph import StateGraph
from langgraph.checkpoint.sqlite import SqliteSaver
from langchain_anthropic
# Define the chatbot state
class ChatbotState(TypedDict):
conversation_history: Annotated[ConversationHistory, operator.add]
pending_human_request: Optional[HumanRequest]
# Create nodes for the chatbot and human
def chatbot(state: ChatbotState):
# TODO
def human(state: ChatbotState):
# TODO
# Create the graph
graph = StateGraph(ChatbotState)
graph.add_node("chatbot", chatbot)
graph.add_node("human", human)
# Define routing logic between chatbot and human
def should_escalate(state):
if state['pending_human_request']:
return "human"
else:
return "chatbot"
graph.add_conditional_edges("chatbot", should_escalate, {
"human": "human",
"chatbot": "chatbot"
})
graph.add_edge("human", "chatbot")
memory = SqliteSaver.from_conn_string(":memory:")
app = graph.compile(checkpointer=memory)
# Run the graph
result = app.invoke(new_user_message)
```
The graph handles all the hard parts:
- `conversation_history` in the state contains the assistant's "memory"
- Conditional edges enable dynamic routing between the chatbot and human based on the chatbot's confidence
- Persistence makes it easy to route to a human so they can respond and resume at any time
With LangGraph, you can build complex, stateful agents without getting bogged down in manual state and interrupt management. Just define your nodes, edges, and state schema - and let the graph take care of the rest.
## Concepts
- [Graphs](concepts.md#graphs)
- [State](concepts.md#state): The data structure passed between nodes, allowing you to persist context
- [Nodes](concepts.#nodes): The building blocks of your graph - LLMs, tools, or custom logic
- [Edges](concepts.md#edges): The connections that define the flow of data between your nodes
- [Conditional Edges](concepts.md#conditional_edges): Special edges that let you dynamically route between nodes based on state
- [Persistence](concepts.md#persistence): Save and resume your graph's state for long-running applications
## How-To Guides
Check out the [How-To Guides](how-tos/index.md) for instructions on handling common tasks with LangGraph.
- Manage State
- Tool Integration
- Human-in-the-Loop
- Async Execution
- Streaming Responses
- Subgraphs & Branching
- Persistence, Visualization, Time Travel
- Benchmarking
## Tutorials
Consult the [Tutorials](tutorials/index.md) to learn more about implementing advanced
- **Agent Executors**: Chat and Langchain agents
- **Planning Agents**: Plan-and-Execute, ReWOO, LLMCompiler
- **Reflection & Critique**: Improving quality via reflection
- **Multi-Agent Systems**: Collaboration, supervision, teams
- **Research & QA**: Web research, retrieval-augmented QA
- **Applications**: Chatbots, code assist, web tasks
- **Evaluation & Analysis**: Simulation, self-discovery, swarms
## Why LangGraph?
LangGraph extends the core strengths of LangChain Runnables (shared interface for streaming, async, and batch calls) to make it easy to:
- Seamless state management across multiple turns of conversation or tool usage
- The ability to flexibly route between nodes based on dynamic criteria
- Smooth switching between LLMs and human intervention
- Persistence for long-running, multi-session applications
If you're building a straightforward DAG,, LangChain expression language is a great fit. But for more complex, stateful applications with nonlinear flows, LangGraph is the perfect tool for the job.
+11
View File
@@ -0,0 +1,11 @@
# Checkpoints
::: langgraph.checkpoint
handler: python
options:
selection:
docstring_style: google
rendering:
heading_level: 3
show_root_toc_entry: false
+11
View File
@@ -0,0 +1,11 @@
# StateGraph
::: langgraph.graph
handler: python
options:
selection:
docstring_style: google
rendering:
heading_level: 3
show_root_toc_entry: false
+11
View File
@@ -0,0 +1,11 @@
# Prebuilt
::: langgraph.prebuilt
handler: python
options:
selection:
docstring_style: google
rendering:
heading_level: 3
show_root_toc_entry: false
+69
View File
@@ -0,0 +1,69 @@
# Tutorials
Welcome to the LangGraph Tutorials! These notebooks provide end-to-end walkthroughs for building various types of language agents and applications using LangGraph.
## Agent Executors
- **Chat Agent (Function Calling)**
- [Base](chat_agent_executor_with_function_calling/base.ipynb): Implementing a chat agent executor with function calling
- [High-Level](chat_agent_executor_with_function_calling/high-level.ipynb): Using the high-level chat agent executor API
- [High-Level Tools](chat_agent_executor_with_function_calling/high-level-tools.ipynb): Integrating tools into the high-level chat agent executor
- **Modifications**
- [Human-in-the-Loop](chat_agent_executor_with_function_calling/human-in-the-loop.ipynb)
- [Force Tool First](chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb)
- [Respond in Format](chat_agent_executor_with_function_calling/respond-in-format.ipynb)
- [Dynamic Direct Return](chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb)
- [Manage Agent Steps](chat_agent_executor_with_function_calling/managing-agent-steps.ipynb)
- **LangChain Agent**
- [Base](agent_executor/base.ipynb): Implementing an agent executor with Langchain agents
- [High-Level](agent_executor/high-level.ipynb): Using the high-level Langchain agent executor API
- **Modifications**
- [Human-in-the-Loop](agent_executor/human-in-the-loop.ipynb)
- [Force Tool First](agent_executor/force-calling-a-tool-first.ipynb)
- [Manage Agent Steps](agent_executor/managing-agent-steps.ipynb)
## Planning Agents
- [Plan-and-Execute](plan-and-execute/plan-and-execute.ipynb): Implementing a basic planning and execution agent
- [Reasoning without Observation](rewoo/rewoo.ipynb): Reducing re-planning by saving observations as variables
- [LLMCompiler](llm-compiler/LLMCompiler.ipynb): Streaming and eagerly executing a DAG of tasks from a planner
## Reflection & Critique
- [Basic Reflection](reflection/reflection.ipynb): Prompting the agent to reflect on and revise its outputs
- [Reflexion](reflexion/reflexion.ipynb): Critiquing missing and superfluous details to guide next steps
- [Language Agent Tree Search](lats/lats.ipynb): Using reflection and rewards to drive a tree search over agents
## Multi-Agent Systems
- [Collaboration](multi_agent/multi-agent-collaboration.ipynb): Enabling two agents to collaborate on a task
- [Supervision](multi_agent/agent_supervisor.ipynb): Using an LLM to orchestrate and delegate to individual agents
- [Hierarchical Teams](multi_agent/hierarchical_agent_teams.ipynb): Orchestrating nested teams of agents to solve problems
## Research & QA
- **Retrieval-Augmented Generation**
- [langgraph_adaptive_rag.ipynb](rag/langgraph_adaptive_rag.ipynb)
- [langgraph_adaptive_rag_cohere.ipynb](rag/langgraph_adaptive_rag_cohere.ipynb)
- [langgraph_adaptive_rag_local.ipynb](rag/langgraph_adaptive_rag_local.ipynb)
- [langgraph_agentic_rag.ipynb](rag/langgraph_agentic_rag.ipynb)
- [langgraph_crag.ipynb](rag/langgraph_crag.ipynb)
- [langgraph_crag_local.ipynb](rag/langgraph_crag_local.ipynb)
- [langgraph_self_rag.ipynb](rag/langgraph_self_rag.ipynb)
- [langgraph_self_rag_local.ipynb](rag/langgraph_self_rag_local.ipynb)
- [Web Research (STORM)](storm/storm.ipynb): Generating Wikipedia-like articles via research and multi-perspective QA
## Applications
- **Chatbots**
- [Customer Support](chatbots/customer-support.ipynb): Building a customer support chatbot
- [Info Gathering](chatbots/information-gather-prompting.ipynb): Building an information gathering chatbot
- [Code Assistant](code_assistant/langgraph_code_assistant.ipynb): Building a code analysis and generation assistant
- [Web Navigation](web-navigation/web_voyager.ipynb): Building an agent that can navigate and interact with websites
## Evaluation & Analysis
- **Chatbot Evaluation via Simulation**
- [Agent-based](chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb): Evaluating chatbots via simulated user interactions
- [Dataset-based](chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb): Evaluating chatbots over a dialog dataset
+197
View File
@@ -0,0 +1,197 @@
site_name: LangGraph
site_description: Build language agents as graphs
site_url: https://langchain-ai.github.io/langgraph/
theme:
name: material
custom_dir: overrides
logo: static/img/brand/wordmark.png
favicon: static/img/brand/favicon.png
features:
- announce.dismiss
- content.action.edit
- content.action.view
- content.code.annotate
- content.code.copy
- content.code.select
- content.tabs.link
- content.tooltips
- header.autohide
- navigation.expand
- navigation.footer
- navigation.indexes
- navigation.instant
- navigation.instant.prefetch
- navigation.instant.progress
- navigation.prune
- navigation.sections
- navigation.tabs
- navigation.top
- navigation.tracking
- search.highlight
- search.share
- search.suggest
- toc.follow
palette:
- scheme: default
primary: white
accent: gray
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: grey
accent: white
toggle:
icon: material/brightness-4
name: Switch to light mode
font:
text: "Public Sans"
code: "Roboto Mono"
plugins:
- search:
separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
- autorefs
- mkdocstrings:
handlers:
python:
options:
members_order: source
allow_inspection: true
show_bases: true
- mkdocs-jupyter:
ignore_h1_titles: true
execute: false
include_source: True
include_requirejs: true
# TODO: Add minify plugin once it works alright with code block copying
# - minify:
# minify_html: true
nav:
- Home:
- 'index.md'
- Quick Start: quick_start.ipynb
- Concepts:
- 'concepts.md'
- "How-to Guides":
- Basics:
- State Management: how-tos/state-model.ipynb
- Tool Integration: how-tos/sql_example.ipynb
- Human-in-the-Loop: how-tos/human-in-the-loop.ipynb
- Performance:
- Async Execution: how-tos/async.ipynb
- Streaming Responses: how-tos/streaming-tokens.ipynb
- Graph Structure:
- Subgraphs: how-tos/subgraph.ipynb
- Branching: how-tos/branching.ipynb
- Development:
- Persistence: how-tos/persistence.ipynb
- Visualization: how-tos/visualization.ipynb
- Time Travel: how-tos/time-travel.ipynb
- Benchmarking: how-tos/swe-bench.ipynb
- Tutorials:
- Agent Executors:
- Chat Agent (Function Calling):
- Base: tutorials/chat_agent_executor_with_function_calling/base.ipynb
- High-Level: tutorials/chat_agent_executor_with_function_calling/high-level.ipynb
- High-Level Tools: tutorials/chat_agent_executor_with_function_calling/high-level-tools.ipynb
- Modifications:
- Human-in-the-Loop: tutorials/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb
- Force Tool First: tutorials/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb
- Respond in Format: tutorials/chat_agent_executor_with_function_calling/respond-in-format.ipynb
- Dynamic Direct Return: tutorials/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb
- Manage Agent Steps: tutorials/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb
- Langchain Agent:
- Base: tutorials/agent_executor/base.ipynb
- High-Level: tutorials/agent_executor/high-level.ipynb
- Modifications:
- Human-in-the-Loop: tutorials/agent_executor/human-in-the-loop.ipynb
- Force Tool First: tutorials/agent_executor/force-calling-a-tool-first.ipynb
- Manage Agent Steps: tutorials/agent_executor/managing-agent-steps.ipynb
- Planning Agents:
- Plan-and-Execute: tutorials/plan-and-execute/plan-and-execute.ipynb
- Reasoning w/o Observation: tutorials/rewoo/rewoo.ipynb
- LLMCompiler: tutorials/llm-compiler/LLMCompiler.ipynb
- Reflection & Critique:
- Basic Reflection: tutorials/reflection/reflection.ipynb
- Reflexion: tutorials/reflexion/reflexion.ipynb
- Language Agent Tree Search: tutorials/lats/lats.ipynb
- Multi-Agent Systems:
- Collaboration: tutorials/multi_agent/multi-agent-collaboration.ipynb
- Supervision: tutorials/multi_agent/agent_supervisor.ipynb
- Hierarchical Teams: tutorials/multi_agent/hierarchical_agent_teams.ipynb
- Research & QA:
- Web Research (STORM): tutorials/storm/storm.ipynb
- Retrieval-Augmented Generation:
- tutorials/rag/langgraph_adaptive_rag.ipynb
- tutorials/rag/langgraph_adaptive_rag_cohere.ipynb
- tutorials/rag/langgraph_adaptive_rag_local.ipynb
- tutorials/rag/langgraph_agentic_rag.ipynb
- tutorials/rag/langgraph_crag.ipynb
- tutorials/rag/langgraph_crag_local.ipynb
- tutorials/rag/langgraph_self_rag.ipynb
- tutorials/rag/langgraph_self_rag_local.ipynb
- Applications:
- Chatbots:
- Customer Support: tutorials/chatbots/customer-support.ipynb
- Info Gathering: tutorials/chatbots/information-gather-prompting.ipynb
- Code Assistant: tutorials/code_assistant/langgraph_code_assistant.ipynb
- Web Navigation: tutorials/web-navigation/web_voyager.ipynb
- Evaluation & Analysis:
- Chatbot Eval via Sim:
- Agent-based: tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb
- Dataset-based: tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb
- Self-Discovering Agent: tutorials/self-discover/self-discover.ipynb
- Swarm: tutorials/gptswarm/swarm.ipynb
- Reference:
- Graphs: reference/graphs.md
- Checkpointing: reference/checkpoints.md
- Prebuilt Components: reference/prebuilt_components.md
markdown_extensions:
- abbr
- admonition
- pymdownx.details
- attr_list
- def_list
- footnotes
- md_in_html
- toc:
permalink: true
- pymdownx.arithmatex:
generic: true
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.details
- pymdownx.emoji:
emoji_generator: !!python/name:material.extensions.emoji.to_svg
emoji_index: !!python/name:material.extensions.emoji.twemoji
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.keys
- pymdownx.magiclink:
normalize_issue_symbols: true
repo_url_shorthand: true
user: langchain-ai
repo: langgraph
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.snippets:
auto_append:
- includes/mkdocs.md
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
combine_header_slug: true
- pymdownx.tasklist:
custom_checkbox: true
extra_css:
- css/mkdocstrings.css
+137
View File
@@ -0,0 +1,137 @@
{% extends "base.html" %}
{% block extrahead %}
<style>
@import url("https://fonts.googleapis.com/css2?family=Public+Sans&display=swap");
:root {
--md-primary-fg-color: #333333;
--md-accent-fg-color: #1E88E5;
--md-default-bg-color: #FFFFFF;
--md-default-fg-color: #333333;
--md-text-font-family: "Public Sans", sans-serif;
}
body {
font-family: var(--md-text-font-family);
background-color: var(--md-default-bg-color);
color: var(--md-default-fg-color);
}
.md-main {
background-color: #FFFFFF;
}
.navbar {
background-color: #FFFFFF;
color: #333333;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.md-footer {
background-color: #F5F5F5;
color: #666666;
}
.md-footer-meta {
background-color: #EEEEEE;
}
.md-typeset a {
color: #1E88E5;
}
.md-typeset a:hover {
color: #1565C0;
}
.md-nav__link--active,
.md-nav__link:active {
color: #1E88E5;
}
.md-search__input {
background-color: #F5F5F5;
color: #333333;
}
.md-search__input:hover,
.md-search__input:focus {
background-color: #EEEEEE;
}
/* Table of contents styles */
.md-nav--secondary .md-nav__item--active > .md-nav__link {
font-weight: bold;
color: var(--md-primary-fg-color);
}
.md-nav--secondary .md-nav__item--nested > .md-nav__link {
font-weight: normal;
color: var(--md-default-fg-color);
}
.md-nav--secondary .md-nav__item--nested > .md-nav__link::before {
content: "";
display: inline-block;
width: 6px;
height: 6px;
background-color: var(--md-default-fg-color);
border-radius: 50%;
margin-right: 0.5rem;
}
[data-md-color-scheme="slate"] {
--md-default-bg-color: #1E1E1E;
--md-default-fg-color: #FFFFFF;
--md-accent-fg-color: #64B5F6;
}
[data-md-color-scheme="slate"] .md-main {
background-color: #1E1E1E;
}
[data-md-color-scheme="slate"] .navbar {
background-color: #1E1E1E;
color: #FFFFFF;
box-shadow: none;
}
[data-md-color-scheme="slate"] .md-footer {
background-color: #1E1E1E;
color: #BDBDBD;
}
[data-md-color-scheme="slate"] .md-footer-meta {
background-color: #1E1E1E;
}
[data-md-color-scheme="slate"] .md-typeset a {
color: #64B5F6;
}
[data-md-color-scheme="slate"] .md-typeset a:hover {
color: #90CAF9;
}
.notebook-links {
display: flex;
justify-content: flex-end;
margin-bottom: 1rem;
}
.notebook-links .md-content__button {
margin-left: 0.5rem;
}
</style>
{% endblock %}
{% block content %}
<div class="notebook-links">
{% if page.nb_url %}
<a href="{{ page.nb_url }}" title="Download Notebook" class="md-content__button md-icon">
{% include ".icons/material/download.svg" %}
</a>
{% endif %}
</div>
{{ super() }}
{% endblock content %}