BAML - A new programming language for building Agents | Vaibhav Gupta, AI By the Bay 2025
Recording: BAML - A new programming language for building Agents | Vaibhav Gupta, AI By the Bay 2025
As you all have heard now, we did something incredibly incredibly silly. We made a programming language. Probably a dumb idea, but maybe by the end of this talk, you might say maybe maybe it's something that we might want. But before we really go talk about a programming language, I want to imagine a world where AI pipelines just kind of work. I think we all see a lot of demos. I see demos on Twitter all day every day. And it's very devoid from reality as far as I can tell of actually using AI in production. Most of the time I use a coding agent produce a slop and sometimes it's good enough that I ship it, but usually it's not really reliable or maintainable from a perspective
And that's the best experience we have with AI. And every now and then I use some product and I'm like man out a form. I'm like why am I doing this? It's just take a picture and fill it out. And we all know why they don't do that because it doesn't work. But what if we lived in a world where that sort of system did work and no matter what you did, no matter how complex the form was, it would effectively just be able to understand these systems. We didn't live in a world of makebelieve, but we lived in a world of reliable systems working off the bat. And I'll zoom in really fast so it's a little bit more clear. And what if we did it a more complicated data set? Like for example, we took really complicated picture of a menu, super blurry
You guys can barely see it given the resolution of the system. And what if the model was still able to go ahead and pull out all the information that needed to? Oops, sorry. I'm really bad at UIs. Let me run a different one. I'll run like a form, for example. It turns out UI coding is still just as hard uh no matter how good the models get. But when you actually go run this, give it a So what's actually going on here? What I'm doing is I'm taking an existing model, no fine-tuning offtheshelf model. I'm giving it a picture of this visa form and I'm asking it to write some code that describe the shape of that visa form
Then I run the code that executes a shape of that visa form and asks the model to pull out all the data for me given that form. And what ends up happening is a two-step process that produces beautiful structured information that's able to take things like arbitrary checkboxes, turn them into booleans, able to take things like an address, turn into a deeply nested data structure. If I'm the government of Cambodia and I'm processing, let's say, a million of these forms every year, I can suddenly turn this whole system into a pipeline of structured data without doing human in the loop eval. The question is how much can we really trust these systems? And that's what I want to talk about today. So how do we do that? Well, we think the idea might be a programming language, but let's talk about why that might be the case. We've all seen software. How many of you have shipped software that fails 5% of the time? That is correct. Zero hands up is the right answer
How many have shipped I'm going to change one word to a lens really fast. I think a lot of us will feel differently if this is what we told our engineering manager that we had this failure rate. And I think we shouldn't feel differently. A lot of these systems, that's why they feel like toys. It's because we ship a failure rate of 5%. We're like, heck yeah. You don't ship a failure rate of 5% anywhere else in the world. So why would you do that here? And the reason that this is so hard is because it's actually a paradigm shift as a software engineer for most people
Most engineering teams have not had to deal with this. Let's say cloud systems or backend API calls. Well, how do you get five nights of uptime on your cloud system? Well, you just use AWS magically. Five nights of uptime. You don't do any work. It just works. Well, that's because AWS has spent tons of effort making distrib. EC2 and S3 are no easy feat
They've taken decades of work to make it work. But now, when you build an AI application, suddenly there's nothing that AWS or OpenAI can do to make your app more fault tolerant. that error we showed in the beginning of this demo. No model provider could have fixed that error. I have to handle an exception somewhere in my UI code for some bite code that I'm not handling correctly. And nothing that AWS or opened could fix that. So all of a sudden as developers we're facing the first problem that we've had to face in decades, which was how do I make my systems error error reliant and change the way I think? Because this these models are no longer going to produce the right answer. they'll sometimes produce the right answer and that is incredibly hard to rationalize between who I am doesn't really but what I think is interesting is we've had this paradigm before we've had this paradigm in the world of web development have some of you written or seen a website that used to be written in this form factor at some point do any of you still see websites written like this >> the batch probably not because you probably see websites written like this and like why why do websites feel more interactive and almost way more alive than they used to feel 10 years ago? Well, that's because React has done a lot of the heavy lifting for the developer
Let's talk about some of the innovation that React created by creating a new file format here. Step one, these slashes over here, if you forgot this slash in this system, you would have a broken website. If you forgot the slash here, you got yelled at by the compiler. compile time error, not a runtime error. If you wanted to rerender this button every single time the count change, it just does it for you. As long as you call the set count variable, React will magically make the button refresh itself. Try doing that here. It's really, really, really hard
And that's why Facebook invented React, because showing like counts and other interactive components on Facebook's homepage ended up being a feat that caused the website to be in a broken state more often than in a working state. And I think lastly, understated value is just pretty color. This stuff is just way nicer to look at than a giant blob of orange. It's way easier on the eyes. It's way easier to semantically just understand what your code is doing. That's really important for building again a reliable, maintainable system over time. And if we add more complexity, then we need our tool chain to take some of the complexity away from ourselves. And that's what React did for websites
It removed some of the complexity and allowed us to work on a higher level of thinking. Now for data science, many of you have probably seen data science and I think if I said what programming languages do data scientists use what's the answer? Python pretty unanimous. Anyone disagree? How are um maybe like 10 years ago but it's definitely Python by this point by volume and I think it shows in GitHub standing and everything but I actually disagree. I think it's not Python. I think it's Jupyter notebook. I don't know any data scientist that has a py file. They have a lot of ipy andb files but very few py files. Why is that? Well, that's because to do data science, what you really need is interactivity
You can run this Python code over and over again, get the right answer. You have to iterate really quickly. Data science is way less about the code and way more about the iteration speed than the tool chain around it. So, every single machine engineer I machine learning engineer I know, scientist I know, has always used Jupyter notebooks. And I think when you think about where LLMs come into play and where this AI world we look in comes into. I think it's somewhere in the hybrid of these two. We need the interactive Jupyter notebooks which is why Python feels so powerful and familiar. Yet we need the reliability and some some of the engineering around these web systems where we used to have giant strings in our codebase that we suddenly got rid of to make it more structured and more compile time analyzable both for tool chains and for um and for actual uh feature enhancement
And when I look at AI code today, I'm sorry for showing you this, uh, but the only word I have to really describe this code is like disgusting, ugly, horrific. I guess any of those words on there. And the reason for that is like it's this code reminds me very very similarly of code that I was showing you not too long ago. this code where I'm just mashing a bunch of strings together and I assemble a of templates to do something. Sometimes I want interactivity. So I put some more jQuery in there, some JavaScript embedded into my back end and maybe it will work. And for simple stuff, it does really work. But the minute you add complexity, you're certainly craving React and it does work a lot better
And I think when I look at this code, I feel the same pains. I see a giant bunch of strings. I see a lot of interruptibility like swapping this call from enthropic to OpenAI is just good luck. Um if you want to go ahead and swap this call everywhere throughout your organization really quickly, good luck. If you want to add some retry logic, probably not too much work. If you want to go add like enhancements on the system like conditionally make decisions on more complicated systems, it's getting harder and harder. And the worst crime here is try testing this system. It's it's virtually impossible
And if we go back to what LM and machine learnings are really about, it goes back to Jupyter notebook where we need interactivity and testability and really quick feedback. But we can't do that here because it's just smashed together in a way that doesn't allow us to design that system. Now, before I show you BAML, I'm going to show you a different way of thinking about LMS that guided us to think might be a good way to model agents. And by the way, you guys have questions, just raise your hand. Go for it. We don't have to wait till the end and we'll talk through it. So, I think a lot of a lot of people right now are trying to sell new vocabulary because whenever you sell new vocabulary, you can sell more tools, which means you can make more money. But I find as an engineer, I've always found it a lot easier to use existing techn existing vocabulary to describe new patterns whenever possible
Because when I can do that, I can leverage my knowledge for the last 10 years and not have to reinvent everything and pretend like I'm a junior engineer from day one again. I'm going to try and describe how we think of agentic development without any new technology required. So let's talk about an LM call. A call to an LLM. I want to model this as just a type signature. It's a system that takes in some message and spits out some type. So in the case of a classifier, it takes in a message which is in the form of a string and spits out either positive, negative or neutral. How it does it doesn't really matter
So we can just ignore that. Treat it like an API contract. Well, I think all of us probably know how to use functions in every single language of our choice. Doesn't matter what language it is. You call the function, you get the thing. And what do we do when the thing doesn't respond with one of the values that are allowed by the type? ideas. You blow up with exception handling and you handle exceptions and as long as the exceptions are well typed, you can go do things with them. So, LM calls are just functions with type safety uh with type signatures
Well, what about tool calls? I personally find tool calling to be one of the most egregious names to for something that is really really what I find much simpler. I want to think of tool calling as if statements and a union. So a tool call is a is an LM function. So a type of type signature that takes in some input of some kind and returns a data model union of data models. The union of data models can be modeled like this. So for example, if the message was what's the weather in San Francisco? Hopefully if the function is behaving correctly, it would pick the get weather data model. It would fill this in. It would say San Francisco
And if the model knows about the zip code of San Francisco, it might even fill in the zip code. But that's up to a type signature to decide if it needs or doesn't need to have that. And then when you actually go ahead and call this code in any language of choice, you have a function call returns a type. You make is of definitions to determine which type you're actually operating on. And you might apply the parameters of that class into a function of your choosing. That's tool calling. So LLMs are function calls. Um uh tool calling is unions plus a for loop
What about parallel tool calling? Well, I think parallel tool calling gets really easy here. You can just it's a union of an array. It's an array of unions. No invention of anything new. And all you have to do here is instead of a single tool, you just put a for loop around your ifs and now you have parallel tool calls. If you want these to run in parallel, well, better hope that all of these are async and then they will also run in parallel. So, we've had to invent almost no new terminology to make tool calling work, parallel tool calling work, and LM calls work in our codebase. But what about agents? Agents are special, but I actually think most of us have written an agent before
How many of you written a tic-tac-toe game at some point in your software engineering life? A couple of you, most of you. I think tic-tac-toe is probably the first agent people have written. What is an agent? Well, to me, what is a tic-tac-toe game? It's a game. You initialize the board game state. You determine whose turn it is. You ask that player to take a turn. You apply that turn that they pooked to the state of the board. If they picked an invalid turn, you reject it
Ask them again. And then you go ahead and determine whose turn it is and go again until some terminal condition. It might be a tie. It might be someone won. It might be maybe they run out of time. An agent is not that different from that. So the way we want we found modeling agents is very similar. You have a function, an lm function who will take state
State can be any type you want it to be. In this case, I've made it an array of messages, but it doesn't have to be. And I'll return some tools. Now, in my Python code, my agent code is really similar. I start my agent with some initial state. I'm initializing my board game. I have I just run my agent forever until something will happen. I ask a model to make the next action which is the turn taking system of the tic-tac-toe game
I use my if statements for my uh from my tool calling system that we talked about earlier. But now instead of the tool calling just doing nothing once I get my state I somehow modify the state that I'm getting into here with some really with whatever mechanism I want and then I let it go again. And sometimes instead of modifying the state I actually just exit out of the loop. And my exit criteria can be really varied. It could be that I want to It could be based on the type. It could be based on cost. It could be based on time. It could be based on the fact that the user sent a control C event
It really varies. But it's just software. And we don't have to invent anything new here to make agents work. And in fact, using old tooling can actually familiarize us with how people have built these sort of systems in the past and then give us the same error recovery mechanisms and all the tool chains around that to make our systems way more reliable without inventing anything new. Any questions? Cool. So then there's this other really scary word. Some of you probably heard it, handoffs. What is a handoff? Well, in this world, handoffs don't really matter because all a handoff is is technically this function could have its own while loop and state inside of itself
And that's a handoff. I've passed on context to another while loop looping system under the hood in some way through the parameters of the function. And now I've kicked off a handoff. And the whole point of designing the system in this way is that when you've actually done this, you can actually think back to software and think how would I design agents from first principles if I did that? And once you ask that question, then we can begin to ask the next question of what do I really need to make agents incredibly powerful and fast to build so we can build not just demos but production systems with 59s of uptime. And that's kind of the point of BML. So I'm going to show you the code because I can't really say I made a programming language without showing the code. Um or else we'd be in demo land again. So let's just start it from scratch
Is everyone trying Python uh as like the host language? So I think one of the most fascinating things about BAML is unlike every other language in the world, BAML is what I would call um almost like a parasitic language. It doesn't actually uh run independently. It runs inside of any existing language of your choice because you shouldn't be limited to using Python if you want to build an AI application. If you're an engine team that has built Java backends forever, your team that has built Go backend for speeds, you should be able to use LMS in those systems too without having to build a Python microser just to go do it. I'll show you exactly how works. Uh make the AI by the bay. CDI by the bay. Um, I'm gonna use UV to initialize my project because UV is a great Python package initializer
I'm going to add BAML to my project. Uh, UV run BAML CLI init. So, I'm going to initialize this Python project to use BAML inside of itself. Uh, I'll I'll use Pyantic along with it as well. And then I'll just open this. And then I will do something that I really really hope you guys are okay with because it makes seeing easy. You're right. >> Yeah
I apologize, but um it is a sin I have to commit. Okay. So what does BML code look like and what are the tool chains that come out of it? What is a tooling that we can build here? Well, the first thing that we do is like I said everything is just a function. Functions parameters functions have return types. In this case, you can see the return type of this function should be a resume data model. Uh but unlike every other model, you can actually go ahead and choose exactly what how you're going to implement the function using a model and a prompt which is slightly different than normal function which are if statements, for loops, etc. So in this case, the model is the OpenAI responses API and pointing to GVD5 mini. I'm going to change this really quickly uh just to use like let's say the OpenAI chat completion API in 40 mini
So I just did that and now I'm going to write this prompt and what this prompt does I'll show really quickly. But the most important part is I'm going to write test cases and we almost force developers to write test cases right away because what we do is we make the tooling so nice that you kind of want to write a test case. What does the tooling do? Well, the first thing the tooling does is it gives you a live preview of the prompt right away. So, no more guessing about what you're sending to the model. You just see it. What else does it do? Well, it highlights input parameters very differently than it highlights a base prompt because you should be able to know what's an input to your function versus what's a base prompt that's being written in without having to do any guesswork. And then the most important thing the tooling actually does is as you go run this, oops, sorry, press the wrong button. As you go run this, we actually give you a run button right here
Just like Jupyter Notebook, we let you run the prompt to see if it's working and giving you the right answer. You can see right over there, the model spit out this text and we turned it into the RSé data model. And this looks like a onetoone transformation. The model gave us the right output. But we all know models don't always work. And secondarily, why are we using JSON? JSON doesn't JSON's like are models naturally good at it. Should we constrain ourselves to that? I don't think so. Try something
Let's add reasoning really quickly to the system before answering. List three reasons why this person is awesome. Example, boom, boom, boom, boom, boom, boom. And this is all the prompt is. You can just see it. There's no guesswork. Um, it's pretty straightforward. Before answering, list three reasons the other person is awesome
And now we'll let the model do it. So the model will list out some reasons. It's pulled out the thing. But what you got out was just the type you wanted. So you don't have to think about this anymore. No matter what the model does, you'll be guaranteed that you get the thing that you typed out here. Let's add some other information. Like for example, do we really need these quotation marks? Probably not
Don't use quotes around keys. Let's run this. So now we got a thing that's not even JSON compliant, not even parsible in any way, but we still pulled out the right information. So what we do behind the scenes is whatever the model gives you, we do our best to apply error correction techniques for you automatically. So you automatically get the type that you have requested from the user. And the whole point of this is when we live in a system of unreliability, what you want is you don't want model call failing or model getting you one thing wrong to fail cause your whole you want to get as much data out of it as possible. So you can at least keep going otherwise your AI costs are going to go up in cost and these systems will fail. It's going to go up in cost latency and you're going to provide a worse user experience and worse uh final output
And let's take this to the next level. What if I want to ignore all boundary ML email.coms and I just don't want to collect them? Well, we can do things here. For example, we can change up the prompt to make this part of a user message roll user. I probably don't need that. So, I've got a user message. I've got a system message up here. Uh, we're going to say a couple more things. We're going to say we're going to add a rule
rule ignore boundary ML boundaryml.com emails and hopefully the model will do the right thing. It did not. Um let's see if we can do better. Well, I think we can. Instead of putting rules scattered throughout my context window, what I could do is what if I could just add context related to my code. So now what I've done here is I've made the code way more readable for a human reading this code or even a coding agent inspecting this code because I know this rule is attached to this field. But I've also made it more closely related to my actual prompt as well because it's way easier for our model to understand. Let's see if this works and it might it might not
I have no idea. And it did. So what happened here is the model gave you a null field which is correct because that's a right value but you requested a string type. So no matter what the no matter what our error correction algorithm did, there's nothing you can do to model a null value into a string type. So in this case, it's your code that is wrong and you need to fix it. You need to make your code have an optional field. Let's run this now and see if this works. So now what the model does is pick null and we correctly got null out of it
Let's do something a little bit more advanced. What if I don't even want the model to make this decision? I just want to ignore all boundaryl email.coms. Well, we can do that really easily. just by leveraging the type system.com not in this. So you can add a type constraint on your field doesn't apply at all to the model. The model has no idea about this. So now you're not biasing the model in in any way and you're letting the model do whatever it needs to only find that you still get null. So you can do you can com kind of marry these ideas of probabistic systems with deterministic rules together in a way that's both easy for your code to understand and leverages your type system
And how does it actually apply in practice? Well, let's take this to I'm going to start gaslighting the model now. So class experience name string ro description string array and I'm going to change this to an experience type and we'll see what the model does really quickly and then I'll show you what to do next as you go around here. So once we've done this, what I can do now is I start gassing the model to say that hey actually I'm going to start lying to the model completely to show you how powerful this error correction technique is where instead of adding the type system through here. I'm actually going to leverage the type system in here. I'm going to change the response to require resume array. The type will actually be an experience array. And now the model has no idea what I actually want. But no matter what, we'll still guarantee that you get the type you want
So the model spit out this. It spit out three values here. And what we did was we gave you an empty array because the most valid form of this data is an array that contains a resume type with no experiences because all of these don't model to the experience type. Now at some point you're going to want to take your code and call it from Python or TypeScript or whatever you have. And this is how easy that ends up being. From BMA client import B ré equals B.ext. Extract resume. And you're watching it autocomplete along the way
Resume dot oops, let's make that a list. Resume experience is going to be a list of experiences in Python. I changed this to a string. Resperience is a list of strings. Now, this is not just true in Python, but it's actually true in every single language out there. Python, TypeScript, uh, Ruby, Java, Go, any language that we support, which is most of them, your type system ends up being mimicked along the way. So now, if you're doing any sort of if you have a front end one language versus back end another, it will just work. It also means that if you end up streaming along the way, bstream.extract uh for R and stream R
name. Uh, sorry. Where did it go? Oops. Did this work? Um, sorry. My Python I made that up. Let me set up in here really fast. Con uh demo.py. If you end up streaming behind the scenes, when you're streaming, name becomes optional
And when you're done streaming, resume.name name becomes a final type. So you get really good type systems across streaming, non-streaming, any sort of variation you want. The whole point of BAML is so you can build really quick, reliable AI pipelines without ever having to think about it. And we can where we do have amazing multiple nines of uptime on your AI systems completely open source works on any um uh any architecture you want it works any language you want it works with any model you want it should it's tooling should should be available everywhere it the tooling I showed you is available on every editor of your choice neoim jet brains vs code forks any of them along the way and really like I said we made a program language it's a very silly decision so we do our best to try and not get in your Uh, Discord Discord response time. For those of you that can't see at the bottom, 50% of messages get five minutes or less response time by real humans. If you don't believe me, ping the Discord. I will respond or someone on the team will respond pretty much any hour of the day. Uh, we get the spike because this is late night hours
If you ping us around like 2 or 3 a.m., you'll get a 5 hour delay. Um, that's where that comes from. And our theory is if you're going to go try this tooling out, we know I I've been an engineer for quite a bit and like I know exactly how I would have responded if someone on my team said let's use this new program language no thanks a try. We generally appreciate it. We've have over a thousand companies using us in prod all the way from government agencies 50s down to tiny startups. You saw today is the syntax of BAML. There's not any more complex than that. So if you could understand the code on the screen, you already know BAML
there's no extra learning curve. That's why every out there is able to use BAML without any fine tuning off the bat. Now, that said, I'm gonna leave you with one last final note. What does BAML stand for? Um, BAML stands for basically a madeup language because it basically is. And why is our logo lamb? Well, lamb and baml actually have the same four letters, lamb. And lambs are extremely cute. And third, most importantly, lambs go baml. Thank you
If you guys want some stickers, come by, say hi, and then I've got I'll happy to answer any questions as well along the way. If you guys like the notion doc that I shared, if you scan the link, we'll send it over right afterwards. But thank you for your time. [applause] >> Name and then question. uh until uh question mobile using that. >> Yeah. >> Can everyone hear me? >> And um so how how well is it correct? >> How well has Rob like war stories? >> I can show you a lot of war stories. I'll show you one
So if you guys want you can look up this blog post. It's got 3.5 years, 12 hard pivots. Uh, still not dead. Um, so I started my startup journey in 2021. Uh, in 2023, I started working at BAML with my co-founder Aaron. We had four users that signed up for some weird reason using us on day one. Um, why they trusted us, God knows, but they did. They're still using us today, which is fun
Um, nine months later, we had 10 users. U, it turns out building a program manager was really, really hard. Way harder than we predicted. U, but it did work at some point. And then since then we've been having pretty steady growth. So the worst are really around like figing out all the architecture issues, fighting up the bindings to every single language. Like the decision to make BAML usable in every programming language itself was really hard. The the error correction algorithm I showed you guys that took eight months to build
It was not a trivial thing to go build out. Uh so all that stuff was really the base layer. And then like since then we've hired some incredible people to come join us. One of our guys wrote a database from scratch for some reason. Why? I have no idea. uh other folks have like been working in like programming languages for the last like 20 years or so. So really just assembling a really stellar team has been taking time uh and I have been surprised by how much of the discord community has been really fun like random mentions of I is this still being recorded? Uh okay well okay I will not say that but the I go to a conference every now and then I'll meet like a company that you would never expect and they'll come up and they'll recognize the shirt like are you the BML guy and we use it every day in prod and they're like a giant company that I would have never um just expected to use this kind of tooling but I think the main thing I've really learned through this journey and really our incentive is just that the tooling in the old world is so so add that and every single project is basically starting from scratch whether in a big company or small company that it's really nice to have amazing tooling and the fact that your backend team whether it's in go or anything else can work with your data science team can work with your front-end team to build a beautiful experience that's actually been surprisingly one of the biggest use cases >> yeah that blog post will probably share a lot more of the pain than I want to share emotionally right Any [laughter] other questions? Yeah. >> Uh Kevin and a lot of the vendors now the structured out
>> Yeah. Yeah. So being able to kind of like provide that type safety along with the request how like kind of like what's the impact for banel and kind of a like a to later or >> it depends like I'll show you something really uh easy which is like this example like we all know tragically you can generate recipes. I bet you could write a structure generation to go do that but what's really hard when you go do this is build an experience like this where it's interactive while you're generating. This is just software. This has nothing to do with LLMs or anything else. But doing this on LMS is incredibly hard. And all you need for that is good tooling
How do you do that? Well, what BAML does under the hood is we generate two type systems. We generate a type system for streaming. We generate a type system for non-streaming. That allows you to build this UI out really easily because now even as you're streaming, you're getting data out. Even if the LM vendors provide structured output, what you really need is incredible tooling to bridge your gap. If your back end is in let's say in Python and your front end is in Typescript just try sending keeping those data structures in sync you will go insane. That's what we do. The fact that the models do this you can do that through BAML
Remember under the hood no matter what any LM framework is telling you all they're doing is making a web request. We just show you the web request that we make and we help you build it and we just do a really good job of doing that and the rest of it is our job which is the tooling side. So when it comes back to like this slide that I shown earlier, where does the burden of responsibility live? You're right. OpenAI can make structured outputs work, but what OpenAI can't do is make your code really freaking clean. That's where BAML helps you. So if clean code matters and you think it helps your shipping velocity, then BAML is a great choice regardless of what uh you use under the hood to get outputs. providers what what I think was is structuration that seems to have a lot >> it does >> you have to I so uh it kind of it's quite hard to adopt things that like fully rely on certain so I'm so don't I it's Yeah. So we don't require structured outputs
Uh what we do is we do no matter what the model returns, we just turn the blob of string into the type you want. That's what we do. So regardless of the model, it just kind of works. So we work on any model that doesn't require structured outputs or does >> or even not JSON in this scenario where it's just like random types. Uh it also means that like structured outputs doesn't support like recursive types schemas with like hundreds of 50 150 fields of nested. We have customers doing like 15 layers of nesting with 150 field extractions like the example I showed at the very beginning with the visa application. This is hard in structured outputs. Good luck
It's not hard here. This is like a dynamic system. So and we also have a dynamic type system under the hood. I'm being told my time is up. If you guys have more questions, come by. I'll be here. Thank you. [applause]