promptflow
261 строка · 11.4 Кб
1<#
2.DESCRIPTION
3Script to build doc site
4
5.EXAMPLE
6PS> ./doc_generation.ps1 -SkipInstall # skip pip install
7PS> ./doc_generation.ps1 -BuildLinkCheck -WarningAsError:$true -SkipInstall
8
9#>
10[CmdletBinding()]
11param(
12[switch]$SkipInstall,
13[switch]$WarningAsError = $false,
14[switch]$BuildLinkCheck = $false,
15[switch]$WithReferenceDoc = $false
16)
17
18[string] $ScriptPath = $PSCommandPath | Split-Path -Parent
19[string] $RepoRootPath = $ScriptPath | Split-Path -Parent | Split-Path -Parent
20[string] $DocPath = [System.IO.Path]::Combine($RepoRootPath, "docs")
21[string] $TempDocPath = New-TemporaryFile | % { Remove-Item $_; New-Item -ItemType Directory -Path $_ }
22[string] $PkgSrcPath = [System.IO.Path]::Combine($RepoRootPath, "src")
23[string] $OutPath = [System.IO.Path]::Combine($ScriptPath, "_build")
24[string] $SphinxApiDoc = [System.IO.Path]::Combine($DocPath, "sphinx_apidoc.log")
25[string] $SphinxBuildDoc = [System.IO.Path]::Combine($DocPath, "sphinx_build.log")
26[string] $WarningErrorPattern = "WARNING:|ERROR:|CRITICAL:| broken "
27[System.Collections.ArrayList]$IncludeList = @("promptflow-tracing", "promptflow-core", "promptflow-devkit", "promptflow-azure", "promptflow-rag", "promptflow-evals")
28$apidocWarningsAndErrors = $null
29$buildWarningsAndErrors = $null
30
31if (-not $SkipInstall){
32# Prepare doc generation packages
33pip install pydata-sphinx-theme==0.11.0
34pip install sphinx==5.1
35pip install sphinx-copybutton==0.5.0
36pip install sphinx_design==0.3.0
37pip install sphinx-sitemap==2.2.0
38pip install sphinx-togglebutton==0.3.2
39pip install sphinxext-rediraffe==0.2.7
40pip install sphinxcontrib-mermaid==0.8.1
41pip install ipython-genutils==0.2.0
42pip install myst-nb==0.17.1
43pip install numpydoc==1.5.0
44pip install myst-parser==0.18.1
45pip install matplotlib==3.4.3
46pip install jinja2==3.0.1
47pip install jupyter-sphinx==0.4.0
48Write-Host "===============Finished install requirements==============="
49}
50
51
52function ProcessFiles {
53# Exclude files not mean to be in doc site
54$exclude_files = "README.md", "dev"
55foreach ($f in $exclude_files)
56{
57$full_path = [System.IO.Path]::Combine($TempDocPath, $f)
58Remove-Item -Path $full_path -Recurse
59}
60}
61
62Write-Host "===============PreProcess Files==============="
63Write-Host "Copy doc to: $TempDocPath"
64ROBOCOPY $DocPath $TempDocPath /S /NFL /NDL /XD "*.git" [System.IO.Path]::Combine($DocPath, "_scripts\_build")
65ProcessFiles
66
67function Update-Sub-Pkg-Index-Title {
68param (
69[string] $SubPkgRefDocPath,
70[string] $SubPkgName
71)
72# This is used to update the title of the promptflow.rst file in the sub package
73# from 'promptflow namespaces' to package name
74$IndexRst = [System.IO.Path]::Combine($SubPkgRefDocPath, "promptflow.rst")
75$IndexContent = Get-Content $IndexRst
76$IndexContent[0] = ("{0} package" -f $SubPkgName)
77$IndexContent[1] = "================================="
78$IndexContent[2] = ".. py:module:: promptflow"
79$IndexContent[3] = " :noindex:"
80Set-Content $IndexRst $IndexContent
81}
82
83function Add-Changelog {
84$ChangelogFolder = [System.IO.Path]::Combine($TempDocPath, "reference\changelog")
85New-Item -ItemType Directory -Path $ChangelogFolder -Force
86Write-Host "===============Collect Package ChangeLog==============="
87$TocTreeContent = @("", "``````{toctree}", ":maxdepth: 1", ":hidden:", "")
88foreach($Item in Get-Childitem -path $PkgSrcPath)
89{
90if((-not ($IncludeList -contains $Item.Name)) -and ($Item.Name -ne "promptflow")){
91continue
92}
93# Collect CHANGELOG, name with package.md
94$ChangelogPath = [System.IO.Path]::Combine($Item.FullName, "CHANGELOG.md")
95$TargetChangelogPath = [System.IO.Path]::Combine($ChangelogFolder, "{0}.md" -f $Item.Name)
96if($Item.Name -ne "promptflow"){
97$TocTreeContent += $Item.name
98}
99Copy-Item -Path $ChangelogPath -Destination $TargetChangelogPath
100}
101$TocTreeContent += "``````"
102# Add subpackage index to promptflow changelog
103$PromptflowChangelog = [System.IO.Path]::Combine($ChangelogFolder, "promptflow.md")
104$PromptflowChangelogContent = Get-Content $PromptflowChangelog
105$PromptflowChangelogContent[0] = "# promptflow package"
106$PromptflowChangelogContent += $TocTreeContent
107Set-Content $PromptflowChangelog $PromptflowChangelogContent
108}
109
110function Add-Api-Reference {
111$RefDocRelativePath = "reference\python-library-reference"
112$RefDocPath = [System.IO.Path]::Combine($TempDocPath, $RefDocRelativePath)
113$PlaceHolderFile = [System.IO.Path]::Combine($RefDocPath, "promptflow.md")
114if(!(Test-Path $RefDocPath)){
115throw "Reference doc path not found. Please make sure '$RefDocRelativePath' is under '$DocPath'"
116}
117Remove-Item $PlaceHolderFile -Force
118$ApidocWarningsAndErrors = [System.Collections.ArrayList]::new()
119foreach($Item in Get-Childitem -path $PkgSrcPath){
120if(-not ($IncludeList -contains $Item.Name)){
121continue
122}
123# Build API reference doc
124$SubPkgPath = [System.IO.Path]::Combine($Item.FullName, "promptflow")
125$SubPkgRefDocPath = [System.IO.Path]::Combine($RefDocPath, $Item.Name)
126Write-Host "===============Build $Item Reference Doc==============="
127$TemplatePath = [System.IO.Path]::Combine($RepoRootPath, "scripts\docs\api_doc_templates")
128sphinx-apidoc --separate --module-first --no-toc --implicit-namespaces "$SubPkgPath" -o "$SubPkgRefDocPath" -t $TemplatePath | Tee-Object -FilePath $SphinxApiDoc
129$SubPkgWarningsAndErrors = Select-String -Path $SphinxApiDoc -Pattern $WarningErrorPattern
130if($SubPkgWarningsAndErrors){
131$ApidocWarningsAndErrors.AddRange($SubPkgWarningsAndErrors)
132}
133Update-Sub-Pkg-Index-Title $SubPkgRefDocPath $Item.Name
134}
135}
136
137function Add-Metadata{
138param (
139[string] $NotebookPath,
140[string] $NotebookRepoPath,
141[System.Collections.ArrayList] $AuthorList
142)
143if (-not $AuthorList){
144# Skip insert if author list not set
145throw "Skip Add Metadata: $NotebookPath - Author list not set"
146return
147}
148$NotebookContent = Get-Content $NotebookPath -Raw | ConvertFrom-Json
149# Covert to System.Collections.ArrayList to avoid 'Collection was of a fixed size' error.
150$NotebookContent.cells = [System.Collections.ArrayList]::new($NotebookContent.cells)
151if($NotebookContent.cells[0].source.Length -gt 1){
152# If the first cell length > 1, indicate there are more things than title it self in the first cell
153throw "Skip Add Metadata: $NotebookPath - First cell length > 1, only leave title to that cell."
154return
155}
156$MetadataFormat = "Authored by: {0}{1}"
157$SingleAuthor = " <a href='https://github.com/{0}' target='_blank'><img src='https://github.com/{0}.png' alt='Avatar' class='avatar dark-light'></a>"
158$JumpLink = "<a href='{0}' target='_blank'><img decoding='async' loading='lazy' src='https://img.shields.io/badge/Open%20on%20GitHub-grey?logo=github' alt='Open on GitHub' class='img_ev3q' style='float: right;'></a>" -f $NotebookRepoPath
159$Authors = $AuthorList | ForEach-Object { $SingleAuthor -f $_.replace("@github.com", "") }
160$Metadata = $MetadataFormat -f ($Authors -join ""), $JumpLink
161# Insert metadata to cells
162$MetadataCell = @{
163"cell_type" = "markdown";
164"metadata" = @{};
165"source" = @($Metadata)
166}
167$NotebookContent.cells.Insert(1, $MetadataCell)
168$NotebookContent | ConvertTo-Json -Depth 100 | Set-Content $NotebookPath
169}
170
171function Add-Notebook
172{
173Write-Host "===============Collect Package Notebooks==============="
174$NotebookRootPath = [System.IO.Path]::Combine($RepoRootPath, "examples")
175$TargetNotebookPath = [System.IO.Path]::Combine($TempDocPath, "tutorials")
176# Create section list
177$SectionNames = "Tracing", "Prompty", "Flow", "Rag"
178$Sections = [ordered]@{
179Tracing=[System.Collections.ArrayList]::new();
180Prompty=[System.Collections.ArrayList]::new();
181Flow=[System.Collections.ArrayList]::new()
182Rag=[System.Collections.ArrayList]::new()
183}
184foreach($Item in Get-Childitem -path $NotebookRootPath -Recurse -Filter "*.ipynb")
185{
186# Notebook to build must have metadata: {"build_doc": {"category": "local/azure"}}
187$NotebookContent = Get-Content $Item.FullName -Raw | ConvertFrom-Json
188if(-not $NotebookContent.metadata.build_doc){
189continue
190}
191$RepoPath = $Item.FullName.Replace($RepoRootPath, "https://github.com/microsoft/promptflow/tree/main/")
192$SectionName = $NotebookContent.metadata.build_doc.section
193[int]$Weight = $NotebookContent.metadata.build_doc.weight
194$Category = $NotebookContent.metadata.build_doc.category
195$AuthorList = $NotebookContent.metadata.build_doc.author
196# If category is 'azure', add 1000 to weight
197if($Category -eq "azure"){
198$Weight += 1000
199}
200# Add ItemName, Category tuple to sections
201$Sections[$SectionName].Add([Tuple]::Create($Item.Name.Replace(".ipynb", ""), $Weight))
202# Copy notebook to doc path
203Write-Host "Adding Notebook $Item ..."
204$MediaDir = $Item.FullName + '\..\media'
205Copy-Item -Path $Item.FullName -Destination $TargetNotebookPath
206if(Test-Path $MediaDir){
207# copy image referenced in notebook
208Write-Host "Copying media files from $MediaDir ..."
209Copy-Item -Path $MediaDir -Destination $TargetNotebookPath -Recurse -Force
210}
211# Append metadata to notebook
212$CopiedNotebookPath = [System.IO.Path]::Combine($TargetNotebookPath, $Item.Name)
213Add-Metadata $CopiedNotebookPath $RepoPath $AuthorList
214}
215# Reverse sort each section list by Weight
216foreach($SectionName in $SectionNames){
217$Sections[$SectionName] = $Sections[$SectionName] | Sort-Object -Property { $_.Item2 }
218}
219$TocTreeContent = @("", "``````{{toctree}}", ":caption: {0}", ":hidden:", ":maxdepth: 1", "", "{1}", "``````")
220# Build toctree content for each section, append to tutorials index.md
221$TutorialIndex = [System.IO.Path]::Combine($TargetNotebookPath, "index.md")
222foreach($SectionName in $SectionNames){
223$SectionTocTree = $TocTreeContent -join "`n"
224# Join Item1 to a string in list
225$ExampleList = ($Sections[$SectionName] | ForEach-Object { $_.Item1 }) -join "`n"
226$SectionTocTree = $SectionTocTree -f $SectionName, $ExampleList
227Write-Debug $SectionTocTree
228Add-Content -Path $TutorialIndex -Value $SectionTocTree
229}
230}
231
232if($WithReferenceDoc){
233Add-Api-Reference
234}
235# Build subpackage changelog
236Add-Changelog
237# Build notebook examples
238Add-Notebook
239
240Write-Host "===============Build Documentation with internal=${Internal}==============="
241$BuildParams = [System.Collections.ArrayList]::new()
242if($WarningAsError){
243$BuildParams.Add("-W")
244$BuildParams.Add("--keep-going")
245}
246if($BuildLinkCheck){
247$BuildParams.Add("-blinkcheck")
248}
249sphinx-build $TempDocPath $OutPath -c $ScriptPath $BuildParams -v | Tee-Object -FilePath $SphinxBuildDoc
250$buildWarningsAndErrors = Select-String -Path $SphinxBuildDoc -Pattern $WarningErrorPattern
251
252Write-Host "Clean path: $TempDocPath"
253Remove-Item $TempDocPath -Recurse -Confirm:$False -Force
254
255
256if ($buildWarningsAndErrors) {
257Write-Host "=============== Build warnings and errors ==============="
258foreach ($line in $buildWarningsAndErrors) {
259Write-Host $line -ForegroundColor Red
260}
261}